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: 193 198 97.5 %
Date: 2019-03-21 Functions: 54 76 71.1 %

          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          14 :   CHECK_EQ(regions.size(), expected_regions.size());
      33             :   auto iter = expected_regions.begin();
      34          50 :   for (auto it = regions.begin(), e = regions.end(); it != e; ++it, ++iter) {
      35          18 :     CHECK_EQ(*it, *iter);
      36             :   }
      37          14 : }
      38             : 
      39           0 : void DisjointAllocationPoolTest::CheckRange(base::AddressRegion region1,
      40             :                                             base::AddressRegion region2) {
      41           3 :   CHECK_EQ(region1, region2);
      42           0 : }
      43             : 
      44           0 : DisjointAllocationPool DisjointAllocationPoolTest::Make(
      45             :     std::initializer_list<base::AddressRegion> regions) {
      46             :   DisjointAllocationPool ret;
      47          49 :   for (auto& region : regions) {
      48          20 :     ret.Merge(region);
      49             :   }
      50           0 :   return ret;
      51             : }
      52             : 
      53       15374 : TEST_F(DisjointAllocationPoolTest, ConstructEmpty) {
      54             :   DisjointAllocationPool a;
      55             :   CHECK(a.IsEmpty());
      56           1 :   CheckPool(a, {});
      57           1 :   a.Merge({1, 4});
      58           1 :   CheckPool(a, {{1, 4}});
      59           1 : }
      60             : 
      61       15374 : TEST_F(DisjointAllocationPoolTest, ConstructWithRange) {
      62             :   DisjointAllocationPool a({1, 4});
      63           1 :   CHECK(!a.IsEmpty());
      64           1 :   CheckPool(a, {{1, 4}});
      65           1 : }
      66             : 
      67       15374 : 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             :   CheckRange(b, {1, 2});
      72           1 :   a.Merge(b);
      73           1 :   CheckPool(a, {{1, 4}});
      74           1 :   CHECK_EQ(a.regions().size(), 1);
      75           1 :   CHECK_EQ(a.regions().front().begin(), 1);
      76           1 :   CHECK_EQ(a.regions().front().end(), 5);
      77           1 : }
      78             : 
      79       15374 : TEST_F(DisjointAllocationPoolTest, ExtractAll) {
      80             :   DisjointAllocationPool a({1, 4});
      81           1 :   base::AddressRegion b = a.Allocate(4);
      82             :   CheckRange(b, {1, 4});
      83           1 :   CHECK(a.IsEmpty());
      84           1 :   a.Merge(b);
      85           1 :   CheckPool(a, {{1, 4}});
      86           1 : }
      87             : 
      88       15374 : 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       15374 : 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       15374 : 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             :   CheckRange(b, {10, 5});
     107           1 : }
     108             : 
     109       15374 : 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       15374 : 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       15374 : 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       15374 : 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       15374 : 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       43036 : std::string PrintWasmCodeManageTestParam(
     145             :     ::testing::TestParamInfo<ModuleStyle> info) {
     146       43036 :   switch (info.param) {
     147             :     case Fixed:
     148       21518 :       return "Fixed";
     149             :     case Growable:
     150       21518 :       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::shared_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          48 :                                      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             :     std::unique_ptr<WasmCode> code = native_module->AddCode(
     179         116 :         index, desc, 0, 0, {}, {}, WasmCode::kFunction, WasmCode::kOther);
     180          58 :     return native_module->PublishCode(std::move(code));
     181             :   }
     182             : 
     183          48 :   size_t page() const { return AllocatePageSize(); }
     184             : 
     185             :   WasmEngine* engine() { return i_isolate()->wasm_engine(); }
     186             : 
     187             :   WasmCodeManager* manager() { return engine()->code_manager(); }
     188             : 
     189             :   void SetMaxCommittedMemory(size_t limit) {
     190          14 :     manager()->SetMaxCommittedMemoryForTesting(limit);
     191             :   }
     192             : };
     193             : 
     194      122960 : INSTANTIATE_TEST_SUITE_P(Parameterized, WasmCodeManagerTest,
     195             :                          ::testing::Values(Fixed, Growable),
     196             :                          PrintWasmCodeManageTestParam);
     197             : 
     198       18450 : TEST_P(WasmCodeManagerTest, EmptyCase) {
     199             :   SetMaxCommittedMemory(0 * page());
     200           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     201             : 
     202           4 :   ASSERT_DEATH_IF_SUPPORTED(AllocModule(1 * page(), GetParam()),
     203             :                             "OOM in NativeModule::AllocateForCode commit");
     204             : }
     205             : 
     206       18450 : TEST_P(WasmCodeManagerTest, AllocateAndGoOverLimit) {
     207             :   SetMaxCommittedMemory(1 * page());
     208           4 :   CHECK_EQ(1 * page(), manager()->remaining_uncommitted_code_space());
     209           4 :   NativeModulePtr native_module = AllocModule(1 * page(), GetParam());
     210           2 :   CHECK(native_module);
     211           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     212             :   uint32_t index = 0;
     213           2 :   WasmCode* code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
     214           2 :   CHECK_NOT_NULL(code);
     215           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     216             : 
     217           2 :   code = AddCode(native_module.get(), index++, 3 * kCodeAlignment);
     218           2 :   CHECK_NOT_NULL(code);
     219           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     220             : 
     221           2 :   code = AddCode(native_module.get(), index++,
     222           2 :                  page() - 4 * kCodeAlignment - kJumpTableSize);
     223           2 :   CHECK_NOT_NULL(code);
     224           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     225             : 
     226             :   // This fails in "reservation" if we cannot extend the code space, or in
     227             :   // "commit" it we can (since we hit the allocation limit in the
     228             :   // WasmCodeManager). Hence don't check for that part of the OOM message.
     229           4 :   ASSERT_DEATH_IF_SUPPORTED(
     230             :       AddCode(native_module.get(), index++, 1 * kCodeAlignment),
     231             :       "OOM in NativeModule::AllocateForCode");
     232             : }
     233             : 
     234       18450 : TEST_P(WasmCodeManagerTest, TotalLimitIrrespectiveOfModuleCount) {
     235           2 :   SetMaxCommittedMemory(3 * page());
     236           4 :   NativeModulePtr nm1 = AllocModule(2 * page(), GetParam());
     237           4 :   NativeModulePtr nm2 = AllocModule(2 * page(), GetParam());
     238           2 :   CHECK(nm1);
     239           2 :   CHECK(nm2);
     240           2 :   WasmCode* code = AddCode(nm1.get(), 0, 2 * page() - kJumpTableSize);
     241           2 :   CHECK_NOT_NULL(code);
     242           4 :   ASSERT_DEATH_IF_SUPPORTED(AddCode(nm2.get(), 0, 2 * page() - kJumpTableSize),
     243             :                             "OOM in NativeModule::AllocateForCode commit");
     244             : }
     245             : 
     246       18450 : TEST_P(WasmCodeManagerTest, GrowingVsFixedModule) {
     247           2 :   SetMaxCommittedMemory(3 * page());
     248           4 :   NativeModulePtr nm = AllocModule(1 * page(), GetParam());
     249           2 :   size_t module_size = GetParam() == Fixed ? kMaxWasmCodeMemory : 1 * page();
     250             :   size_t remaining_space_in_module = module_size - kJumpTableSize;
     251           2 :   if (GetParam() == Fixed) {
     252             :     // Requesting more than the remaining space fails because the module cannot
     253             :     // grow.
     254           2 :     ASSERT_DEATH_IF_SUPPORTED(
     255             :         AddCode(nm.get(), 0, remaining_space_in_module + kCodeAlignment),
     256             :         "OOM in NativeModule::AllocateForCode");
     257             :   } else {
     258             :     // The module grows by one page. One page remains uncommitted.
     259           1 :     CHECK_NOT_NULL(
     260             :         AddCode(nm.get(), 0, remaining_space_in_module + kCodeAlignment));
     261           1 :     CHECK_EQ(manager()->remaining_uncommitted_code_space(), 1 * page());
     262             :   }
     263             : }
     264             : 
     265       18450 : TEST_P(WasmCodeManagerTest, CommitIncrements) {
     266           2 :   SetMaxCommittedMemory(10 * page());
     267           4 :   NativeModulePtr nm = AllocModule(3 * page(), GetParam());
     268           2 :   WasmCode* code = AddCode(nm.get(), 0, kCodeAlignment);
     269           2 :   CHECK_NOT_NULL(code);
     270           4 :   CHECK_EQ(manager()->remaining_uncommitted_code_space(), 9 * page());
     271           2 :   code = AddCode(nm.get(), 1, 2 * page());
     272           2 :   CHECK_NOT_NULL(code);
     273           4 :   CHECK_EQ(manager()->remaining_uncommitted_code_space(), 7 * page());
     274           2 :   code = AddCode(nm.get(), 2, page() - kCodeAlignment - kJumpTableSize);
     275           2 :   CHECK_NOT_NULL(code);
     276           4 :   CHECK_EQ(manager()->remaining_uncommitted_code_space(), 7 * page());
     277           2 : }
     278             : 
     279       18450 : TEST_P(WasmCodeManagerTest, Lookup) {
     280           2 :   SetMaxCommittedMemory(2 * page());
     281             : 
     282           4 :   NativeModulePtr nm1 = AllocModule(1 * page(), GetParam());
     283           4 :   NativeModulePtr nm2 = AllocModule(1 * page(), GetParam());
     284           2 :   WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
     285           2 :   CHECK_EQ(nm1.get(), code1_0->native_module());
     286           2 :   WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
     287           2 :   WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
     288           2 :   WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
     289           2 :   CHECK_EQ(nm2.get(), code2_1->native_module());
     290             : 
     291           2 :   CHECK_EQ(0, code1_0->index());
     292           2 :   CHECK_EQ(1, code1_1->index());
     293           2 :   CHECK_EQ(0, code2_0->index());
     294           2 :   CHECK_EQ(1, code2_1->index());
     295             : 
     296             :   // we know the manager object is allocated here, so we shouldn't
     297             :   // find any WasmCode* associated with that ptr.
     298             :   WasmCode* not_found =
     299           2 :       manager()->LookupCode(reinterpret_cast<Address>(manager()));
     300           2 :   CHECK_NULL(not_found);
     301           2 :   WasmCode* found = manager()->LookupCode(code1_0->instruction_start());
     302           2 :   CHECK_EQ(found, code1_0);
     303           2 :   found = manager()->LookupCode(code2_1->instruction_start() +
     304           4 :                                 (code2_1->instructions().size() / 2));
     305           2 :   CHECK_EQ(found, code2_1);
     306           2 :   found = manager()->LookupCode(code2_1->instruction_start() +
     307           2 :                                 code2_1->instructions().size() - 1);
     308           2 :   CHECK_EQ(found, code2_1);
     309           2 :   found = manager()->LookupCode(code2_1->instruction_start() +
     310           2 :                                 code2_1->instructions().size());
     311           2 :   CHECK_NULL(found);
     312             :   Address mid_code1_1 =
     313           2 :       code1_1->instruction_start() + (code1_1->instructions().size() / 2);
     314           2 :   CHECK_EQ(code1_1, manager()->LookupCode(mid_code1_1));
     315             :   nm1.reset();
     316           2 :   CHECK_NULL(manager()->LookupCode(mid_code1_1));
     317           2 : }
     318             : 
     319       18450 : TEST_P(WasmCodeManagerTest, LookupWorksAfterRewrite) {
     320           2 :   SetMaxCommittedMemory(2 * page());
     321             : 
     322           4 :   NativeModulePtr nm1 = AllocModule(1 * page(), GetParam());
     323             : 
     324           2 :   WasmCode* code0 = AddCode(nm1.get(), 0, kCodeAlignment);
     325           2 :   WasmCode* code1 = AddCode(nm1.get(), 1, kCodeAlignment);
     326           2 :   CHECK_EQ(0, code0->index());
     327           2 :   CHECK_EQ(1, code1->index());
     328           2 :   CHECK_EQ(code1, manager()->LookupCode(code1->instruction_start()));
     329           2 :   WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
     330           2 :   CHECK_EQ(1, code1_1->index());
     331           2 :   CHECK_EQ(code1, manager()->LookupCode(code1->instruction_start()));
     332           2 :   CHECK_EQ(code1_1, manager()->LookupCode(code1_1->instruction_start()));
     333           2 : }
     334             : 
     335             : }  // namespace wasm_heap_unittest
     336             : }  // namespace wasm
     337             : }  // namespace internal
     338        9222 : }  // namespace v8

Generated by: LCOV version 1.10