LCOV - code coverage report
Current view: top level - src/wasm - wasm-engine.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 203 278 73.0 %
Date: 2019-04-19 Functions: 41 52 78.8 %

          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 "src/wasm/wasm-engine.h"
       6             : 
       7             : #include "src/code-tracer.h"
       8             : #include "src/compilation-statistics.h"
       9             : #include "src/counters.h"
      10             : #include "src/objects-inl.h"
      11             : #include "src/objects/heap-number.h"
      12             : #include "src/objects/js-promise.h"
      13             : #include "src/ostreams.h"
      14             : #include "src/wasm/function-compiler.h"
      15             : #include "src/wasm/module-compiler.h"
      16             : #include "src/wasm/module-decoder.h"
      17             : #include "src/wasm/module-instantiate.h"
      18             : #include "src/wasm/streaming-decoder.h"
      19             : #include "src/wasm/wasm-objects-inl.h"
      20             : 
      21             : namespace v8 {
      22             : namespace internal {
      23             : namespace wasm {
      24             : 
      25             : namespace {
      26             : // A task to log a set of {WasmCode} objects in an isolate. It does not own any
      27             : // data itself, since it is owned by the platform, so lifetime is not really
      28             : // bound to the wasm engine.
      29             : class LogCodesTask : public Task {
      30             :  public:
      31             :   LogCodesTask(base::Mutex* mutex, LogCodesTask** task_slot, Isolate* isolate,
      32             :                WasmEngine* engine)
      33             :       : mutex_(mutex),
      34             :         task_slot_(task_slot),
      35             :         isolate_(isolate),
      36           4 :         engine_(engine) {
      37             :     DCHECK_NOT_NULL(task_slot);
      38             :     DCHECK_NOT_NULL(isolate);
      39             :   }
      40             : 
      41           8 :   ~LogCodesTask() {
      42             :     // If the platform deletes this task before executing it, we also deregister
      43             :     // it to avoid use-after-free from still-running background threads.
      44           4 :     if (!cancelled()) DeregisterTask();
      45           4 :   }
      46             : 
      47           0 :   void Run() override {
      48           0 :     if (cancelled()) return;
      49           0 :     DeregisterTask();
      50           0 :     engine_->LogOutstandingCodesForIsolate(isolate_);
      51             :   }
      52             : 
      53             :   void Cancel() {
      54             :     // Cancel will only be called on Isolate shutdown, which happens on the
      55             :     // Isolate's foreground thread. Thus no synchronization needed.
      56           4 :     isolate_ = nullptr;
      57             :   }
      58             : 
      59             :   bool cancelled() const { return isolate_ == nullptr; }
      60             : 
      61           0 :   void DeregisterTask() {
      62             :     // The task will only be deregistered from the foreground thread (executing
      63             :     // this task or calling its destructor), thus we do not need synchronization
      64             :     // on this field access.
      65           0 :     if (task_slot_ == nullptr) return;  // already deregistered.
      66             :     // Remove this task from the {IsolateInfo} in the engine. The next
      67             :     // logging request will allocate and schedule a new task.
      68           0 :     base::MutexGuard guard(mutex_);
      69             :     DCHECK_EQ(this, *task_slot_);
      70           0 :     *task_slot_ = nullptr;
      71           0 :     task_slot_ = nullptr;
      72             :   }
      73             : 
      74             :  private:
      75             :   // The mutex of the WasmEngine.
      76             :   base::Mutex* const mutex_;
      77             :   // The slot in the WasmEngine where this LogCodesTask is stored. This is
      78             :   // cleared by this task before execution or on task destruction.
      79             :   LogCodesTask** task_slot_;
      80             :   Isolate* isolate_;
      81             :   WasmEngine* const engine_;
      82             : };
      83             : 
      84           0 : class WasmGCForegroundTask : public Task {
      85             :  public:
      86           0 :   explicit WasmGCForegroundTask(Isolate* isolate) : isolate_(isolate) {
      87             :     DCHECK_NOT_NULL(isolate);
      88             :   }
      89             : 
      90           0 :   void Run() final {
      91           0 :     if (isolate_ == nullptr) return;  // cancelled.
      92             :     WasmEngine* engine = isolate_->wasm_engine();
      93             :     // If the foreground task is executing, there is no wasm code active. Just
      94             :     // report an empty set of live wasm code.
      95           0 :     engine->ReportLiveCodeForGC(isolate_, Vector<WasmCode*>{});
      96             :   }
      97             : 
      98           0 :   void Cancel() { isolate_ = nullptr; }
      99             : 
     100             :  private:
     101             :   Isolate* isolate_;
     102             : };
     103             : 
     104             : }  // namespace
     105             : 
     106           0 : struct WasmEngine::CurrentGCInfo {
     107             :   // Set of isolates that did not scan their stack yet for used WasmCode, and
     108             :   // their scheduled foreground task.
     109             :   std::unordered_map<Isolate*, WasmGCForegroundTask*> outstanding_isolates;
     110             : 
     111             :   // Set of dead code. Filled with all potentially dead code on initialization.
     112             :   // Code that is still in-use is removed by the individual isolates.
     113             :   std::unordered_set<WasmCode*> dead_code;
     114             : };
     115             : 
     116      124854 : struct WasmEngine::IsolateInfo {
     117       62442 :   explicit IsolateInfo(Isolate* isolate)
     118       62442 :       : log_codes(WasmCode::ShouldBeLogged(isolate)) {
     119             :     v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
     120       62442 :     v8::Platform* platform = V8::GetCurrentPlatform();
     121      124884 :     foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
     122       62442 :   }
     123             : 
     124             : #ifdef DEBUG
     125             :   ~IsolateInfo() {
     126             :     // Before destructing, the {WasmEngine} must have cleared outstanding code
     127             :     // to log.
     128             :     DCHECK_EQ(0, code_to_log.size());
     129             :   }
     130             : #endif
     131             : 
     132             :   // All native modules that are being used by this Isolate (currently only
     133             :   // grows, never shrinks).
     134             :   std::set<NativeModule*> native_modules;
     135             : 
     136             :   // Caches whether code needs to be logged on this isolate.
     137             :   bool log_codes;
     138             : 
     139             :   // The currently scheduled LogCodesTask.
     140             :   LogCodesTask* log_codes_task = nullptr;
     141             : 
     142             :   // The vector of code objects that still need to be logged in this isolate.
     143             :   std::vector<WasmCode*> code_to_log;
     144             : 
     145             :   // The foreground task runner of the isolate (can be called from background).
     146             :   std::shared_ptr<v8::TaskRunner> foreground_task_runner;
     147             : };
     148             : 
     149     1242407 : struct WasmEngine::NativeModuleInfo {
     150             :   // Set of isolates using this NativeModule.
     151             :   std::unordered_set<Isolate*> isolates;
     152             : 
     153             :   // Set of potentially dead code. The ref-count of these code objects was
     154             :   // incremented for each Isolate that might still execute the code, and is
     155             :   // decremented on {RemoveIsolate} or on a GC.
     156             :   std::unordered_set<WasmCode*> potentially_dead_code;
     157             : };
     158             : 
     159       61044 : WasmEngine::WasmEngine()
     160      244176 :     : code_manager_(&memory_tracker_, FLAG_wasm_max_code_space * MB) {}
     161             : 
     162      239556 : WasmEngine::~WasmEngine() {
     163             :   // Synchronize on all background compile tasks.
     164       59889 :   background_compile_task_manager_.CancelAndWait();
     165             :   // All AsyncCompileJobs have been canceled.
     166             :   DCHECK(async_compile_jobs_.empty());
     167             :   // All Isolates have been deregistered.
     168             :   DCHECK(isolates_.empty());
     169             :   // All NativeModules did die.
     170             :   DCHECK(native_modules_.empty());
     171       59889 : }
     172             : 
     173      127850 : bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
     174             :                               const ModuleWireBytes& bytes) {
     175             :   // TODO(titzer): remove dependency on the isolate.
     176      255700 :   if (bytes.start() == nullptr || bytes.length() == 0) return false;
     177             :   ModuleResult result =
     178      255700 :       DecodeWasmModule(enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
     179      255700 :                        isolate->counters(), allocator());
     180             :   return result.ok();
     181             : }
     182             : 
     183        2465 : MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
     184             :     Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
     185             :     Vector<const byte> asm_js_offset_table_bytes,
     186             :     Handle<HeapNumber> uses_bitset) {
     187             :   ModuleResult result =
     188        4930 :       DecodeWasmModule(kAsmjsWasmFeatures, bytes.start(), bytes.end(), false,
     189        4930 :                        kAsmJsOrigin, isolate->counters(), allocator());
     190        2465 :   if (result.failed()) {
     191             :     // This happens once in a while when we have missed some limit check
     192             :     // in the asm parser. Output an error message to help diagnose, but crash.
     193             :     std::cout << result.error().message();
     194           0 :     UNREACHABLE();
     195             :   }
     196             : 
     197             :   // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
     198             :   // in {CompileToNativeModule}.
     199             :   Handle<FixedArray> export_wrappers;
     200             :   std::shared_ptr<NativeModule> native_module =
     201             :       CompileToNativeModule(isolate, kAsmjsWasmFeatures, thrower,
     202        4930 :                             std::move(result).value(), bytes, &export_wrappers);
     203        2465 :   if (!native_module) return {};
     204             : 
     205             :   // Create heap objects for asm.js offset table to be stored in the module
     206             :   // object.
     207             :   Handle<ByteArray> asm_js_offset_table =
     208        2465 :       isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length());
     209             :   asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.start(),
     210             :                                asm_js_offset_table_bytes.length());
     211             : 
     212             :   return AsmWasmData::New(isolate, std::move(native_module), export_wrappers,
     213        4930 :                           asm_js_offset_table, uses_bitset);
     214             : }
     215             : 
     216        5018 : Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs(
     217             :     Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
     218             :     Handle<Script> script) {
     219             :   std::shared_ptr<NativeModule> native_module =
     220             :       asm_wasm_data->managed_native_module()->get();
     221             :   Handle<FixedArray> export_wrappers =
     222        5018 :       handle(asm_wasm_data->export_wrappers(), isolate);
     223             :   size_t code_size_estimate =
     224             :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
     225        5018 :           native_module->module());
     226             : 
     227             :   Handle<WasmModuleObject> module_object =
     228             :       WasmModuleObject::New(isolate, std::move(native_module), script,
     229       10036 :                             export_wrappers, code_size_estimate);
     230        5018 :   module_object->set_asm_js_offset_table(asm_wasm_data->asm_js_offset_table());
     231       10036 :   return module_object;
     232             : }
     233             : 
     234      151368 : MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
     235             :     Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
     236             :     const ModuleWireBytes& bytes) {
     237             :   ModuleResult result =
     238      302735 :       DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
     239      302745 :                        isolate->counters(), allocator());
     240      151367 :   if (result.failed()) {
     241             :     thrower->CompileFailed(result.error());
     242        9082 :     return {};
     243             :   }
     244             : 
     245             :   // Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
     246             :   // in {CompileToModuleObject}.
     247             :   Handle<FixedArray> export_wrappers;
     248             :   std::shared_ptr<NativeModule> native_module =
     249             :       CompileToNativeModule(isolate, enabled, thrower,
     250      284577 :                             std::move(result).value(), bytes, &export_wrappers);
     251      142292 :   if (!native_module) return {};
     252             : 
     253             :   Handle<Script> script =
     254      134771 :       CreateWasmScript(isolate, bytes, native_module->module()->source_map_url);
     255             :   size_t code_size_estimate =
     256             :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
     257      134771 :           native_module->module());
     258             : 
     259             :   // Create the module object.
     260             :   // TODO(clemensh): For the same module (same bytes / same hash), we should
     261             :   // only have one WasmModuleObject. Otherwise, we might only set
     262             :   // breakpoints on a (potentially empty) subset of the instances.
     263             : 
     264             :   // Create the compiled module object and populate with compiled functions
     265             :   // and information needed at instantiation time. This object needs to be
     266             :   // serializable. Instantiation may occur off a deserialized version of this
     267             :   // object.
     268             :   Handle<WasmModuleObject> module_object =
     269             :       WasmModuleObject::New(isolate, std::move(native_module), script,
     270      269542 :                             export_wrappers, code_size_estimate);
     271             : 
     272             :   // Finish the Wasm script now and make it public to the debugger.
     273      134771 :   isolate->debug()->OnAfterCompile(script);
     274      134771 :   return module_object;
     275             : }
     276             : 
     277      134023 : MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
     278             :     Isolate* isolate, ErrorThrower* thrower,
     279             :     Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
     280             :     MaybeHandle<JSArrayBuffer> memory) {
     281             :   return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
     282      136491 :                                      memory);
     283             : }
     284             : 
     285        2468 : void WasmEngine::AsyncInstantiate(
     286             :     Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
     287             :     Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
     288        1120 :   ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
     289             :   // Instantiate a TryCatch so that caught exceptions won't progagate out.
     290             :   // They will still be set as pending exceptions on the isolate.
     291             :   // TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke
     292             :   // start function and report thrown exception explicitly via out argument.
     293        3588 :   v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
     294        2468 :   catcher.SetVerbose(false);
     295        2468 :   catcher.SetCaptureMessage(false);
     296             : 
     297             :   MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
     298             :       isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
     299             : 
     300        2468 :   if (!instance_object.is_null()) {
     301        2696 :     resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
     302        1348 :     return;
     303             :   }
     304             : 
     305        1120 :   if (isolate->has_pending_exception()) {
     306             :     // The JS code executed during instantiation has thrown an exception.
     307             :     // We have to move the exception to the promise chain.
     308             :     Handle<Object> exception(isolate->pending_exception(), isolate);
     309             :     isolate->clear_pending_exception();
     310          48 :     *isolate->external_caught_exception_address() = false;
     311          48 :     resolver->OnInstantiationFailed(exception);
     312          48 :     thrower.Reset();
     313             :   } else {
     314             :     DCHECK(thrower.error());
     315        1072 :     resolver->OnInstantiationFailed(thrower.Reify());
     316             :   }
     317             : }
     318             : 
     319        2403 : void WasmEngine::AsyncCompile(
     320             :     Isolate* isolate, const WasmFeatures& enabled,
     321             :     std::shared_ptr<CompilationResultResolver> resolver,
     322             :     const ModuleWireBytes& bytes, bool is_shared) {
     323        2403 :   if (!FLAG_wasm_async_compilation) {
     324             :     // Asynchronous compilation disabled; fall back on synchronous compilation.
     325           0 :     ErrorThrower thrower(isolate, "WasmCompile");
     326             :     MaybeHandle<WasmModuleObject> module_object;
     327           0 :     if (is_shared) {
     328             :       // Make a copy of the wire bytes to avoid concurrent modification.
     329           0 :       std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
     330             :       memcpy(copy.get(), bytes.start(), bytes.length());
     331           0 :       ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
     332           0 :       module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
     333             :     } else {
     334             :       // The wire bytes are not shared, OK to use them directly.
     335           0 :       module_object = SyncCompile(isolate, enabled, &thrower, bytes);
     336             :     }
     337           0 :     if (thrower.error()) {
     338           0 :       resolver->OnCompilationFailed(thrower.Reify());
     339           0 :       return;
     340             :     }
     341           0 :     Handle<WasmModuleObject> module = module_object.ToHandleChecked();
     342           0 :     resolver->OnCompilationSucceeded(module);
     343           0 :     return;
     344             :   }
     345             : 
     346        2403 :   if (FLAG_wasm_test_streaming) {
     347             :     std::shared_ptr<StreamingDecoder> streaming_decoder =
     348             :         StartStreamingCompilation(isolate, enabled,
     349             :                                   handle(isolate->context(), isolate),
     350         400 :                                   std::move(resolver));
     351         200 :     streaming_decoder->OnBytesReceived(bytes.module_bytes());
     352         200 :     streaming_decoder->Finish();
     353             :     return;
     354             :   }
     355             :   // Make a copy of the wire bytes in case the user program changes them
     356             :   // during asynchronous compilation.
     357        2203 :   std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
     358             :   memcpy(copy.get(), bytes.start(), bytes.length());
     359             : 
     360        4406 :   AsyncCompileJob* job = CreateAsyncCompileJob(
     361             :       isolate, enabled, std::move(copy), bytes.length(),
     362        2203 :       handle(isolate->context(), isolate), std::move(resolver));
     363        2203 :   job->Start();
     364             : }
     365             : 
     366         460 : std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
     367             :     Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
     368             :     std::shared_ptr<CompilationResultResolver> resolver) {
     369             :   AsyncCompileJob* job =
     370         920 :       CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
     371         460 :                             0, context, std::move(resolver));
     372         460 :   return job->CreateStreamingDecoder();
     373             : }
     374             : 
     375          16 : void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
     376             :                                  uint32_t function_index, ExecutionTier tier) {
     377             :   // Note we assume that "one-off" compilations can discard detected features.
     378          16 :   WasmFeatures detected = kNoWasmFeatures;
     379             :   WasmCompilationUnit::CompileWasmFunction(
     380             :       isolate, native_module, &detected,
     381          32 :       &native_module->module()->functions[function_index], tier);
     382          16 : }
     383             : 
     384           0 : std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
     385             :     Handle<WasmModuleObject> module_object) {
     386           0 :   return module_object->shared_native_module();
     387             : }
     388             : 
     389         156 : Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
     390             :     Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module) {
     391         156 :   NativeModule* native_module = shared_native_module.get();
     392             :   ModuleWireBytes wire_bytes(native_module->wire_bytes());
     393             :   const WasmModule* module = native_module->module();
     394             :   Handle<Script> script =
     395         156 :       CreateWasmScript(isolate, wire_bytes, module->source_map_url);
     396         156 :   size_t code_size = native_module->committed_code_space();
     397             :   Handle<WasmModuleObject> module_object = WasmModuleObject::New(
     398         312 :       isolate, std::move(shared_native_module), script, code_size);
     399         312 :   CompileJsToWasmWrappers(isolate, native_module->module(),
     400         156 :                           handle(module_object->export_wrappers(), isolate));
     401             :   {
     402         156 :     base::MutexGuard lock(&mutex_);
     403             :     DCHECK_EQ(1, isolates_.count(isolate));
     404             :     isolates_[isolate]->native_modules.insert(native_module);
     405             :     DCHECK_EQ(1, native_modules_.count(native_module));
     406             :     native_modules_[native_module]->isolates.insert(isolate);
     407             :   }
     408         156 :   return module_object;
     409             : }
     410             : 
     411           0 : CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
     412           0 :   base::MutexGuard guard(&mutex_);
     413           0 :   if (compilation_stats_ == nullptr) {
     414           0 :     compilation_stats_.reset(new CompilationStatistics());
     415             :   }
     416           0 :   return compilation_stats_.get();
     417             : }
     418             : 
     419           0 : void WasmEngine::DumpAndResetTurboStatistics() {
     420           0 :   base::MutexGuard guard(&mutex_);
     421           0 :   if (compilation_stats_ != nullptr) {
     422           0 :     StdoutStream os;
     423           0 :     os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
     424             :   }
     425           0 :   compilation_stats_.reset();
     426           0 : }
     427             : 
     428          64 : CodeTracer* WasmEngine::GetCodeTracer() {
     429          64 :   base::MutexGuard guard(&mutex_);
     430          68 :   if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
     431          64 :   return code_tracer_.get();
     432             : }
     433             : 
     434        2663 : AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
     435             :     Isolate* isolate, const WasmFeatures& enabled,
     436             :     std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
     437             :     std::shared_ptr<CompilationResultResolver> resolver) {
     438             :   AsyncCompileJob* job =
     439             :       new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
     440       10652 :                           context, std::move(resolver));
     441             :   // Pass ownership to the unique_ptr in {async_compile_jobs_}.
     442        2663 :   base::MutexGuard guard(&mutex_);
     443        5326 :   async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
     444        5326 :   return job;
     445             : }
     446             : 
     447        2651 : std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
     448             :     AsyncCompileJob* job) {
     449        2651 :   base::MutexGuard guard(&mutex_);
     450             :   auto item = async_compile_jobs_.find(job);
     451             :   DCHECK(item != async_compile_jobs_.end());
     452             :   std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
     453             :   async_compile_jobs_.erase(item);
     454        2651 :   return result;
     455             : }
     456             : 
     457       56158 : bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
     458       56158 :   base::MutexGuard guard(&mutex_);
     459             :   DCHECK_EQ(1, isolates_.count(isolate));
     460       56158 :   for (auto& entry : async_compile_jobs_) {
     461        3065 :     if (entry.first->isolate() == isolate) return true;
     462             :   }
     463             :   return false;
     464             : }
     465             : 
     466       62435 : void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
     467             :   // Under the mutex get all jobs to delete. Then delete them without holding
     468             :   // the mutex, such that deletion can reenter the WasmEngine.
     469       62437 :   std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
     470             :   {
     471       62435 :     base::MutexGuard guard(&mutex_);
     472             :     DCHECK_EQ(1, isolates_.count(isolate));
     473       62449 :     for (auto it = async_compile_jobs_.begin();
     474             :          it != async_compile_jobs_.end();) {
     475          12 :       if (it->first->isolate() != isolate) {
     476             :         ++it;
     477             :         continue;
     478             :       }
     479          12 :       jobs_to_delete.push_back(std::move(it->second));
     480             :       it = async_compile_jobs_.erase(it);
     481             :     }
     482             :   }
     483       62437 : }
     484             : 
     485       62442 : void WasmEngine::AddIsolate(Isolate* isolate) {
     486       62442 :   base::MutexGuard guard(&mutex_);
     487             :   DCHECK_EQ(0, isolates_.count(isolate));
     488       62442 :   isolates_.emplace(isolate, base::make_unique<IsolateInfo>(isolate));
     489             : 
     490             :   // Install sampling GC callback.
     491             :   // TODO(v8:7424): For now we sample module sizes in a GC callback. This will
     492             :   // bias samples towards apps with high memory pressure. We should switch to
     493             :   // using sampling based on regular intervals independent of the GC.
     494             :   auto callback = [](v8::Isolate* v8_isolate, v8::GCType type,
     495      137668 :                      v8::GCCallbackFlags flags, void* data) {
     496       68834 :     Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
     497             :     Counters* counters = isolate->counters();
     498             :     WasmEngine* engine = isolate->wasm_engine();
     499       68834 :     base::MutexGuard lock(&engine->mutex_);
     500             :     DCHECK_EQ(1, engine->isolates_.count(isolate));
     501      762350 :     for (auto* native_module : engine->isolates_[isolate]->native_modules) {
     502      693516 :       native_module->SampleCodeSize(counters, NativeModule::kSampling);
     503             :     }
     504      137668 :   };
     505       62442 :   isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
     506       62442 :                                          nullptr);
     507       62442 : }
     508             : 
     509       62427 : void WasmEngine::RemoveIsolate(Isolate* isolate) {
     510       62427 :   base::MutexGuard guard(&mutex_);
     511             :   auto it = isolates_.find(isolate);
     512             :   DCHECK_NE(isolates_.end(), it);
     513       62427 :   std::unique_ptr<IsolateInfo> info = std::move(it->second);
     514             :   isolates_.erase(it);
     515       62600 :   for (NativeModule* native_module : info->native_modules) {
     516             :     DCHECK_EQ(1, native_modules_.count(native_module));
     517             :     DCHECK_EQ(1, native_modules_[native_module]->isolates.count(isolate));
     518             :     auto* info = native_modules_[native_module].get();
     519             :     info->isolates.erase(isolate);
     520         173 :     if (current_gc_info_) {
     521             :       auto it = current_gc_info_->outstanding_isolates.find(isolate);
     522           0 :       if (it != current_gc_info_->outstanding_isolates.end()) {
     523           0 :         if (auto* gc_task = it->second) gc_task->Cancel();
     524             :         current_gc_info_->outstanding_isolates.erase(it);
     525             :       }
     526           0 :       for (WasmCode* code : info->potentially_dead_code) {
     527             :         current_gc_info_->dead_code.erase(code);
     528             :       }
     529             :     }
     530             :   }
     531       62427 :   if (auto* task = info->log_codes_task) task->Cancel();
     532       62427 :   if (!info->code_to_log.empty()) {
     533           0 :     WasmCode::DecrementRefCount(VectorOf(info->code_to_log));
     534             :     info->code_to_log.clear();
     535             :   }
     536       62427 : }
     537             : 
     538      323784 : void WasmEngine::LogCode(WasmCode* code) {
     539      323784 :   base::MutexGuard guard(&mutex_);
     540      323860 :   NativeModule* native_module = code->native_module();
     541             :   DCHECK_EQ(1, native_modules_.count(native_module));
     542      647060 :   for (Isolate* isolate : native_modules_[native_module]->isolates) {
     543             :     DCHECK_EQ(1, isolates_.count(isolate));
     544             :     IsolateInfo* info = isolates_[isolate].get();
     545      323200 :     if (info->log_codes == false) continue;
     546           7 :     if (info->log_codes_task == nullptr) {
     547             :       auto new_task = base::make_unique<LogCodesTask>(
     548           4 :           &mutex_, &info->log_codes_task, isolate, this);
     549           4 :       info->log_codes_task = new_task.get();
     550          12 :       info->foreground_task_runner->PostTask(std::move(new_task));
     551           4 :       isolate->stack_guard()->RequestLogWasmCode();
     552             :     }
     553           7 :     info->code_to_log.push_back(code);
     554           7 :     code->IncRef();
     555             :   }
     556      323859 : }
     557             : 
     558         813 : void WasmEngine::EnableCodeLogging(Isolate* isolate) {
     559         813 :   base::MutexGuard guard(&mutex_);
     560             :   auto it = isolates_.find(isolate);
     561             :   DCHECK_NE(isolates_.end(), it);
     562         813 :   it->second->log_codes = true;
     563         813 : }
     564             : 
     565           4 : void WasmEngine::LogOutstandingCodesForIsolate(Isolate* isolate) {
     566             :   // If by now we should not log code any more, do not log it.
     567           4 :   if (!WasmCode::ShouldBeLogged(isolate)) return;
     568             : 
     569             :   // Under the mutex, get the vector of wasm code to log. Then log and decrement
     570             :   // the ref count without holding the mutex.
     571             :   std::vector<WasmCode*> code_to_log;
     572             :   {
     573           4 :     base::MutexGuard guard(&mutex_);
     574             :     DCHECK_EQ(1, isolates_.count(isolate));
     575             :     code_to_log.swap(isolates_[isolate]->code_to_log);
     576             :   }
     577           4 :   if (code_to_log.empty()) return;
     578          11 :   for (WasmCode* code : code_to_log) {
     579           7 :     code->LogCode(isolate);
     580             :   }
     581           4 :   WasmCode::DecrementRefCount(VectorOf(code_to_log));
     582             : }
     583             : 
     584     1242400 : std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
     585             :     Isolate* isolate, const WasmFeatures& enabled, size_t code_size_estimate,
     586             :     bool can_request_more, std::shared_ptr<const WasmModule> module) {
     587             :   std::shared_ptr<NativeModule> native_module =
     588             :       code_manager_.NewNativeModule(this, isolate, enabled, code_size_estimate,
     589     2484807 :                                     can_request_more, std::move(module));
     590     1242407 :   base::MutexGuard lock(&mutex_);
     591     1242407 :   auto pair = native_modules_.insert(std::make_pair(
     592     2484814 :       native_module.get(), base::make_unique<NativeModuleInfo>()));
     593             :   DCHECK(pair.second);  // inserted new entry.
     594             :   pair.first->second.get()->isolates.insert(isolate);
     595     2484814 :   isolates_[isolate]->native_modules.insert(native_module.get());
     596     1242407 :   return native_module;
     597             : }
     598             : 
     599     1242407 : void WasmEngine::FreeNativeModule(NativeModule* native_module) {
     600             :   {
     601     1242407 :     base::MutexGuard guard(&mutex_);
     602             :     auto it = native_modules_.find(native_module);
     603             :     DCHECK_NE(native_modules_.end(), it);
     604     2484793 :     for (Isolate* isolate : it->second->isolates) {
     605             :       DCHECK_EQ(1, isolates_.count(isolate));
     606             :       IsolateInfo* info = isolates_[isolate].get();
     607             :       DCHECK_EQ(1, info->native_modules.count(native_module));
     608             :       info->native_modules.erase(native_module);
     609             :       // If there are {WasmCode} objects of the deleted {NativeModule}
     610             :       // outstanding to be logged in this isolate, remove them. Decrementing the
     611             :       // ref count is not needed, since the {NativeModule} dies anyway.
     612             :       size_t remaining = info->code_to_log.size();
     613     1242386 :       if (remaining > 0) {
     614           0 :         for (size_t i = 0; i < remaining; ++i) {
     615           0 :           while (i < remaining &&
     616           0 :                  info->code_to_log[i]->native_module() == native_module) {
     617             :             // Move the last remaining item to this slot (this can be the same
     618             :             // as {i}, which is OK).
     619           0 :             info->code_to_log[i] = info->code_to_log[--remaining];
     620             :           }
     621             :         }
     622           0 :         info->code_to_log.resize(remaining);
     623             :       }
     624             :     }
     625             :     native_modules_.erase(it);
     626             :   }
     627     1242407 :   code_manager_.FreeNativeModule(native_module);
     628     1242407 : }
     629             : 
     630             : namespace {
     631         639 : class SampleTopTierCodeSizeTask : public CancelableTask {
     632             :  public:
     633             :   SampleTopTierCodeSizeTask(Isolate* isolate,
     634             :                             std::weak_ptr<NativeModule> native_module)
     635             :       : CancelableTask(isolate),
     636             :         isolate_(isolate),
     637         213 :         native_module_(std::move(native_module)) {}
     638             : 
     639         211 :   void RunInternal() override {
     640         211 :     if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
     641         209 :       native_module->SampleCodeSize(isolate_->counters(),
     642         209 :                                     NativeModule::kAfterTopTier);
     643             :     }
     644         211 :   }
     645             : 
     646             :  private:
     647             :   Isolate* const isolate_;
     648             :   const std::weak_ptr<NativeModule> native_module_;
     649             : };
     650             : }  // namespace
     651             : 
     652         214 : void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
     653             :     const std::shared_ptr<NativeModule>& native_module) {
     654         214 :   base::MutexGuard lock(&mutex_);
     655             :   DCHECK_EQ(1, native_modules_.count(native_module.get()));
     656         428 :   for (Isolate* isolate : native_modules_[native_module.get()]->isolates) {
     657             :     DCHECK_EQ(1, isolates_.count(isolate));
     658             :     IsolateInfo* info = isolates_[isolate].get();
     659         213 :     info->foreground_task_runner->PostTask(
     660         639 :         base::make_unique<SampleTopTierCodeSizeTask>(isolate, native_module));
     661             :   }
     662         214 : }
     663             : 
     664           0 : void WasmEngine::ReportLiveCodeForGC(Isolate* isolate,
     665             :                                      Vector<WasmCode*> live_code) {
     666           0 :   base::MutexGuard guard(&mutex_);
     667             :   DCHECK_NOT_NULL(current_gc_info_);
     668             :   auto outstanding_isolate_it =
     669             :       current_gc_info_->outstanding_isolates.find(isolate);
     670             :   DCHECK_NE(current_gc_info_->outstanding_isolates.end(),
     671             :             outstanding_isolate_it);
     672           0 :   auto* fg_task = outstanding_isolate_it->second;
     673           0 :   if (fg_task) fg_task->Cancel();
     674             :   current_gc_info_->outstanding_isolates.erase(outstanding_isolate_it);
     675           0 :   for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
     676             : 
     677           0 :   if (current_gc_info_->outstanding_isolates.empty()) {
     678             :     std::unordered_map<NativeModule*, std::vector<WasmCode*>>
     679             :         dead_code_per_native_module;
     680           0 :     for (WasmCode* code : current_gc_info_->dead_code) {
     681           0 :       dead_code_per_native_module[code->native_module()].push_back(code);
     682             :     }
     683           0 :     for (auto& entry : dead_code_per_native_module) {
     684           0 :       entry.first->FreeCode(VectorOf(entry.second));
     685             :     }
     686             :     current_gc_info_.reset();
     687             :   }
     688           0 : }
     689             : 
     690       76627 : bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
     691       76627 :   base::MutexGuard guard(&mutex_);
     692      153312 :   auto it = native_modules_.find(code->native_module());
     693             :   DCHECK_NE(native_modules_.end(), it);
     694             :   auto added = it->second->potentially_dead_code.insert(code);
     695       76656 :   if (!added.second) return false;  // An entry already existed.
     696       76656 :   new_potentially_dead_code_size_ += code->instructions().size();
     697             :   // Trigger a GC if 1MiB plus 10% of committed code are potentially dead.
     698       76656 :   size_t dead_code_limit = 1 * MB + code_manager_.committed_code_space() / 10;
     699       76656 :   if (FLAG_wasm_code_gc && new_potentially_dead_code_size_ > dead_code_limit &&
     700             :       !current_gc_info_) {
     701           0 :     TriggerGC();
     702             :   }
     703             :   return true;
     704             : }
     705             : 
     706           0 : void WasmEngine::TriggerGC() {
     707             :   DCHECK_NULL(current_gc_info_);
     708             :   DCHECK(FLAG_wasm_code_gc);
     709           0 :   current_gc_info_.reset(new CurrentGCInfo());
     710             :   // Add all potentially dead code to this GC, and trigger a GC task in each
     711             :   // isolate.
     712             :   // TODO(clemensh): Also trigger a stack check interrupt.
     713           0 :   for (auto& entry : native_modules_) {
     714             :     NativeModuleInfo* info = entry.second.get();
     715           0 :     if (info->potentially_dead_code.empty()) continue;
     716           0 :     for (auto* isolate : native_modules_[entry.first]->isolates) {
     717             :       auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
     718           0 :       if (!gc_task) {
     719             :         auto new_task = base::make_unique<WasmGCForegroundTask>(isolate);
     720           0 :         gc_task = new_task.get();
     721             :         DCHECK_EQ(1, isolates_.count(isolate));
     722           0 :         isolates_[isolate]->foreground_task_runner->PostTask(
     723           0 :             std::move(new_task));
     724             :       }
     725             :     }
     726           0 :     for (WasmCode* code : info->potentially_dead_code) {
     727             :       current_gc_info_->dead_code.insert(code);
     728             :     }
     729             :   }
     730           0 : }
     731             : 
     732             : namespace {
     733             : 
     734      244285 : DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
     735             :                                 GetSharedWasmEngine)
     736             : 
     737             : }  // namespace
     738             : 
     739             : // static
     740       61016 : void WasmEngine::InitializeOncePerProcess() {
     741       61016 :   if (!FLAG_wasm_shared_engine) return;
     742      122032 :   *GetSharedWasmEngine() = std::make_shared<WasmEngine>();
     743             : }
     744             : 
     745             : // static
     746       59903 : void WasmEngine::GlobalTearDown() {
     747       59903 :   if (!FLAG_wasm_shared_engine) return;
     748       59903 :   GetSharedWasmEngine()->reset();
     749             : }
     750             : 
     751             : // static
     752       62358 : std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
     753       62358 :   if (FLAG_wasm_shared_engine) return *GetSharedWasmEngine();
     754             :   return std::make_shared<WasmEngine>();
     755             : }
     756             : 
     757             : // {max_mem_pages} is declared in wasm-limits.h.
     758     1651626 : uint32_t max_mem_pages() {
     759             :   STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
     760     3303252 :   return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
     761             : }
     762             : 
     763             : // {max_table_init_entries} is declared in wasm-limits.h.
     764        6415 : uint32_t max_table_init_entries() {
     765             :   return std::min(uint32_t{kV8MaxWasmTableInitEntries},
     766       12830 :                   FLAG_wasm_max_table_size);
     767             : }
     768             : 
     769             : }  // namespace wasm
     770             : }  // namespace internal
     771      122036 : }  // namespace v8

Generated by: LCOV version 1.10