LCOV - code coverage report
Current view: top level - test/cctest/wasm - test-wasm-shared-engine.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 189 193 97.9 %
Date: 2019-01-20 Functions: 34 39 87.2 %

          Line data    Source code
       1             : // Copyright 2018 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 <memory>
       6             : 
       7             : #include "src/microtask-queue.h"
       8             : #include "src/objects-inl.h"
       9             : #include "src/wasm/function-compiler.h"
      10             : #include "src/wasm/wasm-engine.h"
      11             : #include "src/wasm/wasm-module-builder.h"
      12             : #include "src/wasm/wasm-module.h"
      13             : #include "src/wasm/wasm-objects-inl.h"
      14             : 
      15             : #include "test/cctest/cctest.h"
      16             : #include "test/common/wasm/test-signatures.h"
      17             : #include "test/common/wasm/wasm-macro-gen.h"
      18             : #include "test/common/wasm/wasm-module-runner.h"
      19             : 
      20             : namespace v8 {
      21             : namespace internal {
      22             : namespace wasm {
      23             : namespace test_wasm_shared_engine {
      24             : 
      25             : // Helper class representing a WebAssembly engine that is capable of being
      26             : // shared between multiple Isolates, sharing the underlying generated code.
      27             : class SharedEngine {
      28             :  public:
      29          35 :   explicit SharedEngine(size_t max_committed = kMaxWasmCodeMemory)
      30          70 :       : wasm_engine_(base::make_unique<WasmEngine>()) {}
      31          35 :   ~SharedEngine() {
      32             :     // Ensure no remaining uses exist.
      33          35 :     CHECK(wasm_engine_.unique());
      34          35 :   }
      35             : 
      36             :   WasmEngine* engine() const { return wasm_engine_.get(); }
      37             :   WasmCodeManager* code_manager() const { return engine()->code_manager(); }
      38             : 
      39             :   int NumberOfExportedEngineUses() const {
      40             :     // This class holds one implicit use itself, which we discount.
      41          30 :     return static_cast<int>(wasm_engine_.use_count()) - 1;
      42             :   }
      43             : 
      44             :   std::shared_ptr<WasmEngine> ExportEngineForSharing() { return wasm_engine_; }
      45             : 
      46             :  private:
      47             :   std::shared_ptr<WasmEngine> wasm_engine_;
      48             : };
      49             : 
      50             : // Helper type definition representing a WebAssembly module shared between
      51             : // multiple Isolates with implicit reference counting.
      52             : using SharedModule = std::shared_ptr<NativeModule>;
      53             : 
      54             : // Helper class representing an Isolate based on a given shared WebAssembly
      55             : // engine available at construction time.
      56             : class SharedEngineIsolate {
      57             :  public:
      58         630 :   explicit SharedEngineIsolate(SharedEngine* engine)
      59         105 :       : isolate_(v8::Isolate::Allocate()) {
      60         210 :     isolate()->SetWasmEngine(engine->ExportEngineForSharing());
      61             :     v8::Isolate::CreateParams create_params;
      62         105 :     create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
      63         105 :     v8::Isolate::Initialize(isolate_, create_params);
      64         105 :     v8::HandleScope handle_scope(v8_isolate());
      65         210 :     v8::Context::New(v8_isolate())->Enter();
      66         105 :     testing::SetupIsolateForWasmModule(isolate());
      67         210 :     zone_.reset(new Zone(isolate()->allocator(), ZONE_NAME));
      68         105 :   }
      69         105 :   ~SharedEngineIsolate() {
      70             :     zone_.reset();
      71         105 :     isolate_->Dispose();
      72         105 :   }
      73             : 
      74             :   Zone* zone() const { return zone_.get(); }
      75             :   v8::Isolate* v8_isolate() { return isolate_; }
      76             :   Isolate* isolate() { return reinterpret_cast<Isolate*>(isolate_); }
      77             : 
      78          70 :   Handle<WasmInstanceObject> CompileAndInstantiate(ZoneBuffer* buffer) {
      79             :     ErrorThrower thrower(isolate(), "CompileAndInstantiate");
      80             :     MaybeHandle<WasmInstanceObject> instance =
      81             :         testing::CompileAndInstantiateForTesting(
      82             :             isolate(), &thrower,
      83          35 :             ModuleWireBytes(buffer->begin(), buffer->end()));
      84          35 :     return instance.ToHandleChecked();
      85             :   }
      86             : 
      87         135 :   Handle<WasmInstanceObject> ImportInstance(SharedModule shared_module) {
      88             :     Handle<WasmModuleObject> module_object =
      89          90 :         isolate()->wasm_engine()->ImportNativeModule(isolate(), shared_module);
      90             :     ErrorThrower thrower(isolate(), "ImportInstance");
      91             :     MaybeHandle<WasmInstanceObject> instance =
      92             :         isolate()->wasm_engine()->SyncInstantiate(isolate(), &thrower,
      93          90 :                                                   module_object, {}, {});
      94          45 :     return instance.ToHandleChecked();
      95             :   }
      96             : 
      97          15 :   SharedModule ExportInstance(Handle<WasmInstanceObject> instance) {
      98          15 :     return instance->module_object()->shared_native_module();
      99             :   }
     100             : 
     101        2555 :   int32_t Run(Handle<WasmInstanceObject> instance) {
     102        2555 :     return testing::RunWasmModuleForTesting(isolate(), instance, 0, nullptr);
     103             :   }
     104             : 
     105             :  private:
     106             :   v8::Isolate* isolate_;
     107             :   std::unique_ptr<Zone> zone_;
     108             : };
     109             : 
     110             : // Helper class representing a Thread running its own instance of an Isolate
     111             : // with a shared WebAssembly engine available at construction time.
     112         120 : class SharedEngineThread : public v8::base::Thread {
     113             :  public:
     114          60 :   SharedEngineThread(SharedEngine* engine,
     115             :                      std::function<void(SharedEngineIsolate&)> callback)
     116             :       : Thread(Options("SharedEngineThread")),
     117             :         engine_(engine),
     118         120 :         callback_(callback) {}
     119             : 
     120          60 :   void Run() override {
     121          60 :     SharedEngineIsolate isolate(engine_);
     122          60 :     callback_(isolate);
     123          60 :   }
     124             : 
     125             :  private:
     126             :   SharedEngine* engine_;
     127             :   std::function<void(SharedEngineIsolate&)> callback_;
     128             : };
     129             : 
     130             : namespace {
     131             : 
     132          45 : ZoneBuffer* BuildReturnConstantModule(Zone* zone, int constant) {
     133          45 :   TestSignatures sigs;
     134             :   ZoneBuffer* buffer = new (zone) ZoneBuffer(zone);
     135          45 :   WasmModuleBuilder* builder = new (zone) WasmModuleBuilder(zone);
     136          45 :   WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
     137          45 :   f->builder()->AddExport(CStrVector("main"), f);
     138          45 :   byte code[] = {WASM_I32V_2(constant)};
     139          45 :   f->EmitCode(code, sizeof(code));
     140          45 :   f->Emit(kExprEnd);
     141          45 :   builder->WriteTo(*buffer);
     142          45 :   return buffer;
     143             : }
     144             : 
     145          20 : class MockInstantiationResolver : public InstantiationResultResolver {
     146             :  public:
     147             :   explicit MockInstantiationResolver(Handle<Object>* out_instance)
     148          10 :       : out_instance_(out_instance) {}
     149          10 :   void OnInstantiationSucceeded(Handle<WasmInstanceObject> result) override {
     150          20 :     *out_instance_->location() = result->ptr();
     151          10 :   }
     152           0 :   void OnInstantiationFailed(Handle<Object> error_reason) override {
     153           0 :     UNREACHABLE();
     154             :   }
     155             : 
     156             :  private:
     157             :   Handle<Object>* out_instance_;
     158             : };
     159             : 
     160          20 : class MockCompilationResolver : public CompilationResultResolver {
     161             :  public:
     162             :   MockCompilationResolver(SharedEngineIsolate& isolate,
     163             :                           Handle<Object>* out_instance)
     164          10 :       : isolate_(isolate), out_instance_(out_instance) {}
     165          10 :   void OnCompilationSucceeded(Handle<WasmModuleObject> result) override {
     166             :     isolate_.isolate()->wasm_engine()->AsyncInstantiate(
     167             :         isolate_.isolate(),
     168             :         base::make_unique<MockInstantiationResolver>(out_instance_), result,
     169          40 :         {});
     170          10 :   }
     171           0 :   void OnCompilationFailed(Handle<Object> error_reason) override {
     172           0 :     UNREACHABLE();
     173             :   }
     174             : 
     175             :  private:
     176             :   SharedEngineIsolate& isolate_;
     177             :   Handle<Object>* out_instance_;
     178             : };
     179             : 
     180          48 : void PumpMessageLoop(SharedEngineIsolate& isolate) {
     181             :   v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
     182             :                                 isolate.v8_isolate(),
     183          24 :                                 platform::MessageLoopBehavior::kWaitForWork);
     184             :   isolate.isolate()->default_microtask_queue()->RunMicrotasks(
     185          24 :       isolate.isolate());
     186          24 : }
     187             : 
     188          10 : Handle<WasmInstanceObject> CompileAndInstantiateAsync(
     189          40 :     SharedEngineIsolate& isolate, ZoneBuffer* buffer) {
     190          10 :   Handle<Object> maybe_instance = handle(Smi::kZero, isolate.isolate());
     191          10 :   auto enabled_features = WasmFeaturesFromIsolate(isolate.isolate());
     192             :   isolate.isolate()->wasm_engine()->AsyncCompile(
     193             :       isolate.isolate(), enabled_features,
     194             :       base::make_unique<MockCompilationResolver>(isolate, &maybe_instance),
     195          30 :       ModuleWireBytes(buffer->begin(), buffer->end()), true);
     196          78 :   while (!maybe_instance->IsWasmInstanceObject()) PumpMessageLoop(isolate);
     197             :   Handle<WasmInstanceObject> instance =
     198          10 :       Handle<WasmInstanceObject>::cast(maybe_instance);
     199          10 :   return instance;
     200             : }
     201             : 
     202             : }  // namespace
     203             : 
     204       28342 : TEST(SharedEngineUseCount) {
     205           5 :   SharedEngine engine;
     206           5 :   CHECK_EQ(0, engine.NumberOfExportedEngineUses());
     207             :   {
     208           5 :     SharedEngineIsolate isolate(&engine);
     209           5 :     CHECK_EQ(1, engine.NumberOfExportedEngineUses());
     210             :   }
     211           5 :   CHECK_EQ(0, engine.NumberOfExportedEngineUses());
     212             :   {
     213           5 :     SharedEngineIsolate isolate1(&engine);
     214           5 :     CHECK_EQ(1, engine.NumberOfExportedEngineUses());
     215          10 :     SharedEngineIsolate isolate2(&engine);
     216          10 :     CHECK_EQ(2, engine.NumberOfExportedEngineUses());
     217             :   }
     218           5 :   CHECK_EQ(0, engine.NumberOfExportedEngineUses());
     219           5 : }
     220             : 
     221       28342 : TEST(SharedEngineRunSeparated) {
     222           5 :   SharedEngine engine;
     223             :   {
     224           5 :     SharedEngineIsolate isolate(&engine);
     225           5 :     HandleScope scope(isolate.isolate());
     226           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
     227           5 :     Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
     228          10 :     CHECK_EQ(23, isolate.Run(instance));
     229             :   }
     230             :   {
     231           5 :     SharedEngineIsolate isolate(&engine);
     232           5 :     HandleScope scope(isolate.isolate());
     233           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
     234           5 :     Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
     235          10 :     CHECK_EQ(42, isolate.Run(instance));
     236           5 :   }
     237           5 : }
     238             : 
     239       28342 : TEST(SharedEngineRunImported) {
     240           5 :   SharedEngine engine;
     241             :   SharedModule module;
     242             :   {
     243           5 :     SharedEngineIsolate isolate(&engine);
     244           5 :     HandleScope scope(isolate.isolate());
     245           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
     246           5 :     Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
     247          10 :     module = isolate.ExportInstance(instance);
     248           5 :     CHECK_EQ(23, isolate.Run(instance));
     249          10 :     CHECK_EQ(2, module.use_count());
     250             :   }
     251           5 :   CHECK_EQ(1, module.use_count());
     252             :   {
     253           5 :     SharedEngineIsolate isolate(&engine);
     254           5 :     HandleScope scope(isolate.isolate());
     255          10 :     Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
     256           5 :     CHECK_EQ(23, isolate.Run(instance));
     257          10 :     CHECK_EQ(2, module.use_count());
     258             :   }
     259          10 :   CHECK_EQ(1, module.use_count());
     260           5 : }
     261             : 
     262       28342 : TEST(SharedEngineRunThreadedBuildingSync) {
     263           5 :   SharedEngine engine;
     264           5 :   SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
     265             :     HandleScope scope(isolate.isolate());
     266           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
     267           5 :     Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
     268           5 :     CHECK_EQ(23, isolate.Run(instance));
     269          20 :   });
     270           5 :   SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
     271             :     HandleScope scope(isolate.isolate());
     272           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
     273           5 :     Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
     274           5 :     CHECK_EQ(42, isolate.Run(instance));
     275          20 :   });
     276           5 :   thread1.Start();
     277           5 :   thread2.Start();
     278           5 :   thread1.Join();
     279          10 :   thread2.Join();
     280           5 : }
     281             : 
     282       28342 : TEST(SharedEngineRunThreadedBuildingAsync) {
     283           5 :   SharedEngine engine;
     284           5 :   SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
     285             :     HandleScope scope(isolate.isolate());
     286           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
     287             :     Handle<WasmInstanceObject> instance =
     288           5 :         CompileAndInstantiateAsync(isolate, buffer);
     289           5 :     CHECK_EQ(23, isolate.Run(instance));
     290          20 :   });
     291           5 :   SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
     292             :     HandleScope scope(isolate.isolate());
     293           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
     294             :     Handle<WasmInstanceObject> instance =
     295           5 :         CompileAndInstantiateAsync(isolate, buffer);
     296           5 :     CHECK_EQ(42, isolate.Run(instance));
     297          20 :   });
     298           5 :   thread1.Start();
     299           5 :   thread2.Start();
     300           5 :   thread1.Join();
     301          10 :   thread2.Join();
     302           5 : }
     303             : 
     304       28342 : TEST(SharedEngineRunThreadedExecution) {
     305           5 :   SharedEngine engine;
     306             :   SharedModule module;
     307             :   {
     308           5 :     SharedEngineIsolate isolate(&engine);
     309           5 :     HandleScope scope(isolate.isolate());
     310           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
     311           5 :     Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
     312          15 :     module = isolate.ExportInstance(instance);
     313             :   }
     314           5 :   SharedEngineThread thread1(&engine, [module](SharedEngineIsolate& isolate) {
     315             :     HandleScope scope(isolate.isolate());
     316          10 :     Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
     317           5 :     CHECK_EQ(23, isolate.Run(instance));
     318          25 :   });
     319           5 :   SharedEngineThread thread2(&engine, [module](SharedEngineIsolate& isolate) {
     320             :     HandleScope scope(isolate.isolate());
     321          10 :     Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
     322           5 :     CHECK_EQ(23, isolate.Run(instance));
     323          25 :   });
     324           5 :   thread1.Start();
     325           5 :   thread2.Start();
     326           5 :   thread1.Join();
     327          10 :   thread2.Join();
     328           5 : }
     329             : 
     330       28342 : TEST(SharedEngineRunThreadedTierUp) {
     331           5 :   SharedEngine engine;
     332             :   SharedModule module;
     333             :   {
     334           5 :     SharedEngineIsolate isolate(&engine);
     335           5 :     HandleScope scope(isolate.isolate());
     336           5 :     ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
     337           5 :     Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
     338          15 :     module = isolate.ExportInstance(instance);
     339             :   }
     340             :   constexpr int kNumberOfThreads = 5;
     341             :   std::list<SharedEngineThread> threads;
     342          30 :   for (int i = 0; i < kNumberOfThreads; ++i) {
     343          25 :     threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
     344             :       constexpr int kNumberOfIterations = 100;
     345             :       HandleScope scope(isolate.isolate());
     346          50 :       Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
     347        2525 :       for (int j = 0; j < kNumberOfIterations; ++j) {
     348        2500 :         CHECK_EQ(23, isolate.Run(instance));
     349             :       }
     350         100 :     });
     351             :   }
     352          10 :   threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
     353             :     HandleScope scope(isolate.isolate());
     354          10 :     Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
     355           5 :     WasmFeatures detected = kNoWasmFeatures;
     356             :     WasmCompilationUnit::CompileWasmFunction(
     357             :         isolate.isolate(), module.get(), &detected,
     358          15 :         &module->module()->functions[0], ExecutionTier::kOptimized);
     359           5 :     CHECK_EQ(23, isolate.Run(instance));
     360          20 :   });
     361          40 :   for (auto& thread : threads) thread.Start();
     362          45 :   for (auto& thread : threads) thread.Join();
     363           5 : }
     364             : 
     365             : }  // namespace test_wasm_shared_engine
     366             : }  // namespace wasm
     367             : }  // namespace internal
     368       85011 : }  // namespace v8

Generated by: LCOV version 1.10