LCOV - code coverage report
Current view: top level - src/wasm - wasm-engine.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 202 272 74.3 %
Date: 2019-04-17 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      124822 : struct WasmEngine::IsolateInfo {
     117       62426 :   explicit IsolateInfo(Isolate* isolate)
     118       62426 :       : log_codes(WasmCode::ShouldBeLogged(isolate)) {
     119             :     v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
     120       62426 :     v8::Platform* platform = V8::GetCurrentPlatform();
     121      124852 :     foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
     122       62426 :   }
     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     1242981 : 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       61028 : WasmEngine::WasmEngine()
     160      244112 :     : code_manager_(&memory_tracker_, FLAG_wasm_max_code_space * MB) {}
     161             : 
     162      239492 : WasmEngine::~WasmEngine() {
     163             :   // Synchronize on all background compile tasks.
     164       59873 :   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       59873 : }
     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        2469 : 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        4938 :       DecodeWasmModule(kAsmjsWasmFeatures, bytes.start(), bytes.end(), false,
     189        4938 :                        kAsmJsOrigin, isolate->counters(), allocator());
     190        2469 :   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        4938 :                             std::move(result).value(), bytes, &export_wrappers);
     203        2469 :   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        2469 :       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        4938 :                           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      151958 : MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
     235             :     Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
     236             :     const ModuleWireBytes& bytes) {
     237             :   ModuleResult result =
     238      303915 :       DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
     239      303919 :                        isolate->counters(), allocator());
     240      151957 :   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      285754 :                             std::move(result).value(), bytes, &export_wrappers);
     251      142879 :   if (!native_module) return {};
     252             : 
     253             :   Handle<Script> script =
     254      135358 :       CreateWasmScript(isolate, bytes, native_module->module()->source_map_url);
     255             :   size_t code_size_estimate =
     256             :       wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
     257      135358 :           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      270716 :                             export_wrappers, code_size_estimate);
     271             : 
     272             :   // Finish the Wasm script now and make it public to the debugger.
     273      135358 :   isolate->debug()->OnAfterCompile(script);
     274      135358 :   return module_object;
     275             : }
     276             : 
     277      134923 : 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      137375 :                                      memory);
     283             : }
     284             : 
     285        2452 : 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        3572 :   v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
     294        2452 :   catcher.SetVerbose(false);
     295        2452 :   catcher.SetCaptureMessage(false);
     296             : 
     297             :   MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
     298             :       isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
     299             : 
     300        2452 :   if (!instance_object.is_null()) {
     301        2664 :     resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
     302        1332 :     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        2395 : void WasmEngine::AsyncCompile(
     320             :     Isolate* isolate, const WasmFeatures& enabled,
     321             :     std::shared_ptr<CompilationResultResolver> resolver,
     322             :     const ModuleWireBytes& bytes, bool is_shared) {
     323        2395 :   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        2395 :   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        2195 :   std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
     358             :   memcpy(copy.get(), bytes.start(), bytes.length());
     359             : 
     360        4390 :   AsyncCompileJob* job = CreateAsyncCompileJob(
     361             :       isolate, enabled, std::move(copy), bytes.length(),
     362        2195 :       handle(isolate->context(), isolate), std::move(resolver));
     363        2195 :   job->Start();
     364             : }
     365             : 
     366         452 : 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         904 :       CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
     371         452 :                             0, context, std::move(resolver));
     372         452 :   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        2647 : 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       10588 :                           context, std::move(resolver));
     441             :   // Pass ownership to the unique_ptr in {async_compile_jobs_}.
     442        2647 :   base::MutexGuard guard(&mutex_);
     443        5294 :   async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
     444        5294 :   return job;
     445             : }
     446             : 
     447        2636 : std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
     448             :     AsyncCompileJob* job) {
     449        2636 :   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        2636 :   return result;
     455             : }
     456             : 
     457       56015 : bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
     458       56015 :   base::MutexGuard guard(&mutex_);
     459             :   DCHECK_EQ(1, isolates_.count(isolate));
     460       56015 :   for (auto& entry : async_compile_jobs_) {
     461        3118 :     if (entry.first->isolate() == isolate) return true;
     462             :   }
     463             :   return false;
     464             : }
     465             : 
     466       62420 : 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       62421 :   std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
     470             :   {
     471       62420 :     base::MutexGuard guard(&mutex_);
     472             :     DCHECK_EQ(1, isolates_.count(isolate));
     473       62435 :     for (auto it = async_compile_jobs_.begin();
     474             :          it != async_compile_jobs_.end();) {
     475          14 :       if (it->first->isolate() != isolate) {
     476             :         ++it;
     477             :         continue;
     478             :       }
     479          11 :       jobs_to_delete.push_back(std::move(it->second));
     480             :       it = async_compile_jobs_.erase(it);
     481             :     }
     482             :   }
     483       62421 : }
     484             : 
     485       62426 : void WasmEngine::AddIsolate(Isolate* isolate) {
     486       62426 :   base::MutexGuard guard(&mutex_);
     487             :   DCHECK_EQ(0, isolates_.count(isolate));
     488       62426 :   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      150650 :                      v8::GCCallbackFlags flags, void* data) {
     496       75325 :     Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
     497             :     Counters* counters = isolate->counters();
     498             :     WasmEngine* engine = isolate->wasm_engine();
     499       75325 :     base::MutexGuard lock(&engine->mutex_);
     500             :     DCHECK_EQ(1, engine->isolates_.count(isolate));
     501      770149 :     for (auto* native_module : engine->isolates_[isolate]->native_modules) {
     502      694824 :       native_module->SampleCodeSize(counters, NativeModule::kSampling);
     503             :     }
     504      150650 :   };
     505       62426 :   isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
     506       62426 :                                          nullptr);
     507       62426 : }
     508             : 
     509       62411 : void WasmEngine::RemoveIsolate(Isolate* isolate) {
     510       62411 :   base::MutexGuard guard(&mutex_);
     511             :   auto it = isolates_.find(isolate);
     512             :   DCHECK_NE(isolates_.end(), it);
     513       62411 :   std::unique_ptr<IsolateInfo> info = std::move(it->second);
     514             :   isolates_.erase(it);
     515       62593 :   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         182 :     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       62411 :   if (auto* task = info->log_codes_task) task->Cancel();
     532       62411 :   if (!info->code_to_log.empty()) {
     533           0 :     WasmCode::DecrementRefCount(VectorOf(info->code_to_log));
     534             :     info->code_to_log.clear();
     535             :   }
     536       62411 : }
     537             : 
     538      323612 : void WasmEngine::LogCode(WasmCode* code) {
     539      323612 :   base::MutexGuard guard(&mutex_);
     540      323674 :   NativeModule* native_module = code->native_module();
     541             :   DCHECK_EQ(1, native_modules_.count(native_module));
     542      646337 :   for (Isolate* isolate : native_modules_[native_module]->isolates) {
     543             :     DCHECK_EQ(1, isolates_.count(isolate));
     544             :     IsolateInfo* info = isolates_[isolate].get();
     545      322663 :     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      323673 : }
     557             : 
     558         812 : void WasmEngine::EnableCodeLogging(Isolate* isolate) {
     559         812 :   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     1242977 : 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     2485958 :                                     can_request_more, std::move(module));
     590     1242981 :   base::MutexGuard lock(&mutex_);
     591     1242981 :   auto pair = native_modules_.insert(std::make_pair(
     592     2485962 :       native_module.get(), base::make_unique<NativeModuleInfo>()));
     593             :   DCHECK(pair.second);  // inserted new entry.
     594             :   pair.first->second.get()->isolates.insert(isolate);
     595     2485962 :   isolates_[isolate]->native_modules.insert(native_module.get());
     596     1242981 :   return native_module;
     597             : }
     598             : 
     599     1242981 : void WasmEngine::FreeNativeModule(NativeModule* native_module) {
     600             :   {
     601     1242981 :     base::MutexGuard guard(&mutex_);
     602             :     auto it = native_modules_.find(native_module);
     603             :     DCHECK_NE(native_modules_.end(), it);
     604     2485932 :     for (Isolate* isolate : it->second->isolates) {
     605             :       DCHECK_EQ(1, isolates_.count(isolate));
     606             :       DCHECK_EQ(1, isolates_[isolate]->native_modules.count(native_module));
     607             :       isolates_[isolate]->native_modules.erase(native_module);
     608             :     }
     609             :     native_modules_.erase(it);
     610             :   }
     611     1242981 :   code_manager_.FreeNativeModule(native_module);
     612     1242981 : }
     613             : 
     614             : namespace {
     615         480 : class SampleTopTierCodeSizeTask : public CancelableTask {
     616             :  public:
     617             :   SampleTopTierCodeSizeTask(Isolate* isolate,
     618             :                             std::weak_ptr<NativeModule> native_module)
     619             :       : CancelableTask(isolate),
     620             :         isolate_(isolate),
     621         160 :         native_module_(std::move(native_module)) {}
     622             : 
     623         158 :   void RunInternal() override {
     624         158 :     if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
     625         154 :       native_module->SampleCodeSize(isolate_->counters(),
     626         154 :                                     NativeModule::kAfterTopTier);
     627             :     }
     628         158 :   }
     629             : 
     630             :  private:
     631             :   Isolate* const isolate_;
     632             :   const std::weak_ptr<NativeModule> native_module_;
     633             : };
     634             : }  // namespace
     635             : 
     636         163 : void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
     637             :     const std::shared_ptr<NativeModule>& native_module) {
     638         163 :   base::MutexGuard lock(&mutex_);
     639             :   DCHECK_EQ(1, native_modules_.count(native_module.get()));
     640         326 :   for (Isolate* isolate : native_modules_[native_module.get()]->isolates) {
     641             :     DCHECK_EQ(1, isolates_.count(isolate));
     642             :     IsolateInfo* info = isolates_[isolate].get();
     643         160 :     info->foreground_task_runner->PostTask(
     644         480 :         base::make_unique<SampleTopTierCodeSizeTask>(isolate, native_module));
     645             :   }
     646         163 : }
     647             : 
     648           0 : void WasmEngine::ReportLiveCodeForGC(Isolate* isolate,
     649             :                                      Vector<WasmCode*> live_code) {
     650           0 :   base::MutexGuard guard(&mutex_);
     651             :   DCHECK_NOT_NULL(current_gc_info_);
     652             :   auto outstanding_isolate_it =
     653             :       current_gc_info_->outstanding_isolates.find(isolate);
     654             :   DCHECK_NE(current_gc_info_->outstanding_isolates.end(),
     655             :             outstanding_isolate_it);
     656           0 :   auto* fg_task = outstanding_isolate_it->second;
     657           0 :   if (fg_task) fg_task->Cancel();
     658             :   current_gc_info_->outstanding_isolates.erase(outstanding_isolate_it);
     659           0 :   for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
     660             : 
     661           0 :   if (current_gc_info_->outstanding_isolates.empty()) {
     662             :     std::unordered_map<NativeModule*, std::vector<WasmCode*>>
     663             :         dead_code_per_native_module;
     664           0 :     for (WasmCode* code : current_gc_info_->dead_code) {
     665           0 :       dead_code_per_native_module[code->native_module()].push_back(code);
     666             :     }
     667           0 :     for (auto& entry : dead_code_per_native_module) {
     668           0 :       entry.first->FreeCode(VectorOf(entry.second));
     669             :     }
     670             :     current_gc_info_.reset();
     671             :   }
     672           0 : }
     673             : 
     674       76792 : bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
     675       76792 :   base::MutexGuard guard(&mutex_);
     676      153644 :   auto it = native_modules_.find(code->native_module());
     677             :   DCHECK_NE(native_modules_.end(), it);
     678             :   auto added = it->second->potentially_dead_code.insert(code);
     679       76822 :   if (!added.second) return false;  // An entry already existed.
     680       76822 :   new_potentially_dead_code_size_ += code->instructions().size();
     681             :   // Trigger a GC if 1MiB plus 10% of committed code are potentially dead.
     682       76822 :   size_t dead_code_limit = 1 * MB + code_manager_.committed_code_space() / 10;
     683       76822 :   if (FLAG_wasm_code_gc && new_potentially_dead_code_size_ > dead_code_limit &&
     684             :       !current_gc_info_) {
     685           0 :     TriggerGC();
     686             :   }
     687             :   return true;
     688             : }
     689             : 
     690           0 : void WasmEngine::TriggerGC() {
     691             :   DCHECK_NULL(current_gc_info_);
     692             :   DCHECK(FLAG_wasm_code_gc);
     693           0 :   current_gc_info_.reset(new CurrentGCInfo());
     694             :   // Add all potentially dead code to this GC, and trigger a GC task in each
     695             :   // isolate.
     696             :   // TODO(clemensh): Also trigger a stack check interrupt.
     697           0 :   for (auto& entry : native_modules_) {
     698             :     NativeModuleInfo* info = entry.second.get();
     699           0 :     if (info->potentially_dead_code.empty()) continue;
     700           0 :     for (auto* isolate : native_modules_[entry.first]->isolates) {
     701             :       auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
     702           0 :       if (!gc_task) {
     703             :         auto new_task = base::make_unique<WasmGCForegroundTask>(isolate);
     704           0 :         gc_task = new_task.get();
     705             :         DCHECK_EQ(1, isolates_.count(isolate));
     706           0 :         isolates_[isolate]->foreground_task_runner->PostTask(
     707           0 :             std::move(new_task));
     708             :       }
     709             :     }
     710           0 :     for (WasmCode* code : info->potentially_dead_code) {
     711             :       current_gc_info_->dead_code.insert(code);
     712             :     }
     713             :   }
     714           0 : }
     715             : 
     716             : namespace {
     717             : 
     718      244221 : DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
     719             :                                 GetSharedWasmEngine)
     720             : 
     721             : }  // namespace
     722             : 
     723             : // static
     724       61000 : void WasmEngine::InitializeOncePerProcess() {
     725       61000 :   if (!FLAG_wasm_shared_engine) return;
     726      122000 :   *GetSharedWasmEngine() = std::make_shared<WasmEngine>();
     727             : }
     728             : 
     729             : // static
     730       59887 : void WasmEngine::GlobalTearDown() {
     731       59887 :   if (!FLAG_wasm_shared_engine) return;
     732       59887 :   GetSharedWasmEngine()->reset();
     733             : }
     734             : 
     735             : // static
     736       62342 : std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
     737       62342 :   if (FLAG_wasm_shared_engine) return *GetSharedWasmEngine();
     738             :   return std::make_shared<WasmEngine>();
     739             : }
     740             : 
     741             : // {max_mem_pages} is declared in wasm-limits.h.
     742     1656994 : uint32_t max_mem_pages() {
     743             :   STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
     744     3313988 :   return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
     745             : }
     746             : 
     747             : // {max_table_init_entries} is declared in wasm-limits.h.
     748        6431 : uint32_t max_table_init_entries() {
     749             :   return std::min(uint32_t{kV8MaxWasmTableInitEntries},
     750       12862 :                   FLAG_wasm_max_table_size);
     751             : }
     752             : 
     753             : }  // namespace wasm
     754             : }  // namespace internal
     755      122004 : }  // namespace v8

Generated by: LCOV version 1.10