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: 202 207 97.6 %
Date: 2019-04-17 Functions: 55 77 71.4 %

          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       15444 : 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       15444 : TEST_F(DisjointAllocationPoolTest, ConstructWithRange) {
      62             :   DisjointAllocationPool a({1, 4});
      63           1 :   CHECK(!a.IsEmpty());
      64           1 :   CheckPool(a, {{1, 4}});
      65           1 : }
      66             : 
      67       15444 : 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       15444 : 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       15444 : 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       15444 : 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       15444 : 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       15444 : 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       15444 : 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       15444 : 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       15444 : 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       15444 : 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       43232 : std::string PrintWasmCodeManageTestParam(
     145             :     ::testing::TestParamInfo<ModuleStyle> info) {
     146       43232 :   switch (info.param) {
     147             :     case Fixed:
     148       21616 :       return "Fixed";
     149             :     case Growable:
     150       21616 :       return "Growable";
     151             :   }
     152           0 :   UNREACHABLE();
     153             : }
     154             : 
     155          28 : 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             :   static size_t page_size;
     162             : 
     163          28 :   WasmCodeManagerTest() {
     164          14 :     if (page_size == 0) page_size = AllocatePageSize();
     165             :     DCHECK_NE(0, page_size);
     166          14 :   }
     167             : 
     168             :   using NativeModulePtr = std::shared_ptr<NativeModule>;
     169             : 
     170          16 :   NativeModulePtr AllocModule(size_t size, ModuleStyle style) {
     171          32 :     std::shared_ptr<WasmModule> module(new WasmModule);
     172          16 :     module->num_declared_functions = kNumFunctions;
     173          16 :     bool can_request_more = style == Growable;
     174             :     return engine()->NewNativeModule(i_isolate(), kAllWasmFeatures, size,
     175          48 :                                      can_request_more, std::move(module));
     176             :   }
     177             : 
     178          29 :   WasmCode* AddCode(NativeModule* native_module, uint32_t index, size_t size) {
     179          29 :     CodeDesc desc;
     180             :     memset(reinterpret_cast<void*>(&desc), 0, sizeof(CodeDesc));
     181          29 :     std::unique_ptr<byte[]> exec_buff(new byte[size]);
     182          29 :     desc.buffer = exec_buff.get();
     183          29 :     desc.instr_size = static_cast<int>(size);
     184             :     std::unique_ptr<WasmCode> code = native_module->AddCode(
     185         116 :         index, desc, 0, 0, {}, {}, WasmCode::kFunction, ExecutionTier::kNone);
     186          58 :     return native_module->PublishCode(std::move(code));
     187             :   }
     188             : 
     189             :   WasmEngine* engine() { return i_isolate()->wasm_engine(); }
     190             : 
     191             :   WasmCodeManager* manager() { return engine()->code_manager(); }
     192             : 
     193             :   void SetMaxCommittedMemory(size_t limit) {
     194          14 :     manager()->SetMaxCommittedMemoryForTesting(limit);
     195             :   }
     196             : };
     197             : 
     198             : // static
     199             : size_t WasmCodeManagerTest::page_size = 0;
     200             : 
     201      123520 : INSTANTIATE_TEST_SUITE_P(Parameterized, WasmCodeManagerTest,
     202             :                          ::testing::Values(Fixed, Growable),
     203             :                          PrintWasmCodeManageTestParam);
     204             : 
     205       18534 : TEST_P(WasmCodeManagerTest, EmptyCase) {
     206             :   SetMaxCommittedMemory(0);
     207           2 :   CHECK_EQ(0, manager()->committed_code_space());
     208             : 
     209           4 :   ASSERT_DEATH_IF_SUPPORTED(AllocModule(page_size, GetParam()),
     210             :                             "OOM in NativeModule::AllocateForCode commit");
     211             : }
     212             : 
     213       18534 : TEST_P(WasmCodeManagerTest, AllocateAndGoOverLimit) {
     214           2 :   SetMaxCommittedMemory(page_size);
     215           2 :   CHECK_EQ(0, manager()->committed_code_space());
     216           2 :   NativeModulePtr native_module = AllocModule(page_size, GetParam());
     217           2 :   CHECK(native_module);
     218           2 :   CHECK_EQ(page_size, manager()->committed_code_space());
     219           4 :   WasmCodeRefScope code_ref_scope;
     220             :   uint32_t index = 0;
     221           2 :   WasmCode* code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
     222           2 :   CHECK_NOT_NULL(code);
     223           2 :   CHECK_EQ(page_size, manager()->committed_code_space());
     224             : 
     225           2 :   code = AddCode(native_module.get(), index++, 3 * kCodeAlignment);
     226           2 :   CHECK_NOT_NULL(code);
     227           2 :   CHECK_EQ(page_size, manager()->committed_code_space());
     228             : 
     229           2 :   code = AddCode(native_module.get(), index++,
     230           2 :                  page_size - 4 * kCodeAlignment - kJumpTableSize);
     231           2 :   CHECK_NOT_NULL(code);
     232           2 :   CHECK_EQ(page_size, manager()->committed_code_space());
     233             : 
     234             :   // This fails in "reservation" if we cannot extend the code space, or in
     235             :   // "commit" it we can (since we hit the allocation limit in the
     236             :   // WasmCodeManager). Hence don't check for that part of the OOM message.
     237           4 :   ASSERT_DEATH_IF_SUPPORTED(
     238             :       AddCode(native_module.get(), index++, 1 * kCodeAlignment),
     239             :       "OOM in NativeModule::AllocateForCode");
     240             : }
     241             : 
     242       18534 : TEST_P(WasmCodeManagerTest, TotalLimitIrrespectiveOfModuleCount) {
     243           2 :   SetMaxCommittedMemory(3 * page_size);
     244           2 :   NativeModulePtr nm1 = AllocModule(2 * page_size, GetParam());
     245           2 :   NativeModulePtr nm2 = AllocModule(2 * page_size, GetParam());
     246           2 :   CHECK(nm1);
     247           2 :   CHECK(nm2);
     248           4 :   WasmCodeRefScope code_ref_scope;
     249           2 :   WasmCode* code = AddCode(nm1.get(), 0, 2 * page_size - kJumpTableSize);
     250           2 :   CHECK_NOT_NULL(code);
     251           4 :   ASSERT_DEATH_IF_SUPPORTED(
     252             :       AddCode(nm2.get(), 0, 2 * page_size - kJumpTableSize),
     253             :       "OOM in NativeModule::AllocateForCode commit");
     254             : }
     255             : 
     256       18534 : TEST_P(WasmCodeManagerTest, GrowingVsFixedModule) {
     257           2 :   SetMaxCommittedMemory(3 * page_size);
     258           2 :   NativeModulePtr nm = AllocModule(page_size, GetParam());
     259           2 :   size_t module_size = GetParam() == Fixed ? kMaxWasmCodeMemory : page_size;
     260             :   size_t remaining_space_in_module = module_size - kJumpTableSize;
     261           2 :   if (GetParam() == Fixed) {
     262             :     // Requesting more than the remaining space fails because the module cannot
     263             :     // grow.
     264           2 :     ASSERT_DEATH_IF_SUPPORTED(
     265             :         AddCode(nm.get(), 0, remaining_space_in_module + kCodeAlignment),
     266             :         "OOM in NativeModule::AllocateForCode");
     267             :   } else {
     268             :     // The module grows by one page. One page remains uncommitted.
     269           2 :     WasmCodeRefScope code_ref_scope;
     270           1 :     CHECK_NOT_NULL(
     271             :         AddCode(nm.get(), 0, remaining_space_in_module + kCodeAlignment));
     272           1 :     CHECK_EQ(2 * page_size, manager()->committed_code_space());
     273             :   }
     274             : }
     275             : 
     276       18534 : TEST_P(WasmCodeManagerTest, CommitIncrements) {
     277           2 :   SetMaxCommittedMemory(10 * page_size);
     278           2 :   NativeModulePtr nm = AllocModule(3 * page_size, GetParam());
     279           4 :   WasmCodeRefScope code_ref_scope;
     280           2 :   WasmCode* code = AddCode(nm.get(), 0, kCodeAlignment);
     281           2 :   CHECK_NOT_NULL(code);
     282           2 :   CHECK_EQ(page_size, manager()->committed_code_space());
     283           2 :   code = AddCode(nm.get(), 1, 2 * page_size);
     284           2 :   CHECK_NOT_NULL(code);
     285           2 :   CHECK_EQ(3 * page_size, manager()->committed_code_space());
     286           2 :   code = AddCode(nm.get(), 2, page_size - kCodeAlignment - kJumpTableSize);
     287           2 :   CHECK_NOT_NULL(code);
     288           2 :   CHECK_EQ(3 * page_size, manager()->committed_code_space());
     289           2 : }
     290             : 
     291       18534 : TEST_P(WasmCodeManagerTest, Lookup) {
     292           2 :   SetMaxCommittedMemory(2 * page_size);
     293             : 
     294           2 :   NativeModulePtr nm1 = AllocModule(page_size, GetParam());
     295           2 :   NativeModulePtr nm2 = AllocModule(page_size, GetParam());
     296             :   Address mid_code1_1;
     297             :   {
     298             :     // The {WasmCodeRefScope} needs to die before {nm1} dies.
     299           4 :     WasmCodeRefScope code_ref_scope;
     300           2 :     WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
     301           2 :     CHECK_EQ(nm1.get(), code1_0->native_module());
     302           2 :     WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
     303           2 :     WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
     304           2 :     WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
     305           2 :     CHECK_EQ(nm2.get(), code2_1->native_module());
     306             : 
     307           2 :     CHECK_EQ(0, code1_0->index());
     308           2 :     CHECK_EQ(1, code1_1->index());
     309           2 :     CHECK_EQ(0, code2_0->index());
     310           2 :     CHECK_EQ(1, code2_1->index());
     311             : 
     312             :     // we know the manager object is allocated here, so we shouldn't
     313             :     // find any WasmCode* associated with that ptr.
     314             :     WasmCode* not_found =
     315           2 :         manager()->LookupCode(reinterpret_cast<Address>(manager()));
     316           2 :     CHECK_NULL(not_found);
     317           2 :     WasmCode* found = manager()->LookupCode(code1_0->instruction_start());
     318           2 :     CHECK_EQ(found, code1_0);
     319           2 :     found = manager()->LookupCode(code2_1->instruction_start() +
     320           4 :                                   (code2_1->instructions().size() / 2));
     321           2 :     CHECK_EQ(found, code2_1);
     322           2 :     found = manager()->LookupCode(code2_1->instruction_start() +
     323           2 :                                   code2_1->instructions().size() - 1);
     324           2 :     CHECK_EQ(found, code2_1);
     325           2 :     found = manager()->LookupCode(code2_1->instruction_start() +
     326           2 :                                   code2_1->instructions().size());
     327           2 :     CHECK_NULL(found);
     328             :     mid_code1_1 =
     329           2 :         code1_1->instruction_start() + (code1_1->instructions().size() / 2);
     330           2 :     CHECK_EQ(code1_1, manager()->LookupCode(mid_code1_1));
     331             :   }
     332             :   nm1.reset();
     333           2 :   CHECK_NULL(manager()->LookupCode(mid_code1_1));
     334           2 : }
     335             : 
     336       18534 : TEST_P(WasmCodeManagerTest, LookupWorksAfterRewrite) {
     337           2 :   SetMaxCommittedMemory(2 * page_size);
     338             : 
     339           2 :   NativeModulePtr nm1 = AllocModule(page_size, GetParam());
     340             : 
     341           4 :   WasmCodeRefScope code_ref_scope;
     342           2 :   WasmCode* code0 = AddCode(nm1.get(), 0, kCodeAlignment);
     343           2 :   WasmCode* code1 = AddCode(nm1.get(), 1, kCodeAlignment);
     344           2 :   CHECK_EQ(0, code0->index());
     345           2 :   CHECK_EQ(1, code1->index());
     346           2 :   CHECK_EQ(code1, manager()->LookupCode(code1->instruction_start()));
     347           2 :   WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
     348           2 :   CHECK_EQ(1, code1_1->index());
     349           2 :   CHECK_EQ(code1, manager()->LookupCode(code1->instruction_start()));
     350           2 :   CHECK_EQ(code1_1, manager()->LookupCode(code1_1->instruction_start()));
     351           2 : }
     352             : 
     353             : }  // namespace wasm_heap_unittest
     354             : }  // namespace wasm
     355             : }  // namespace internal
     356        9264 : }  // namespace v8

Generated by: LCOV version 1.10