Bug 1546934 - Change LCov code coverage machinery to use a process-wide flag. r=nbp
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 25 Apr 2019 13:35:55 +0000
changeset 530140 24740ab9a7266f84283b2beeebe665edf903f09f
parent 530139 a47ffeaa2467cf4a927fc0bc225b2b7f2f6911f4
child 530141 2222d7465ff705104ba61283d0a12302ea332048
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1546934
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1546934 - Change LCov code coverage machinery to use a process-wide flag. r=nbp This will simplify the Baseline Interpreter work. Differential Revision: https://phabricator.services.mozilla.com/D28813
js/src/frontend/BytecodeCompiler.cpp
js/src/gc/GC.cpp
js/src/shell/js.cpp
js/src/vm/CodeCoverage.cpp
js/src/vm/CodeCoverage.h
js/src/vm/Initialization.cpp
js/src/vm/JSContext.h
js/src/vm/JSScript.cpp
js/src/vm/Realm.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -412,17 +412,17 @@ bool BytecodeCompiler::assignSource(Sour
 
   return true;
 }
 
 bool BytecodeCompiler::canLazilyParse() const {
   return options.canLazilyParse &&
          !cx->realm()->behaviors().disableLazyParsing() &&
          !cx->realm()->behaviors().discardSource() && !options.sourceIsLazy &&
-         !cx->lcovEnabled() &&
+         !coverage::IsLCovEnabled() &&
          // Disabled during record/replay. The replay debugger requires
          // scripts to be constructed in a consistent order, which might not
          // happen with lazy parsing.
          !mozilla::recordreplay::IsRecordingOrReplaying();
 }
 
 template <typename Unit>
 bool frontend::SourceAwareCompiler<Unit>::createSourceAndParser(
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -8170,17 +8170,17 @@ void GCRuntime::mergeRealms(Realm* sourc
   target->zone()->types.typeLifoAlloc().transferFrom(
       &source->zone()->types.typeLifoAlloc());
   MOZ_RELEASE_ASSERT(source->zone()->types.sweepTypeLifoAlloc.ref().isEmpty());
 
   // Atoms which are marked in source's zone are now marked in target's zone.
   atomMarking.adoptMarkedAtoms(target->zone(), source->zone());
 
   // Merge script name maps in the target realm's map.
-  if (rt->lcovOutput().isEnabled() && source->scriptNameMap) {
+  if (coverage::IsLCovEnabled() && source->scriptNameMap) {
     AutoEnterOOMUnsafeRegion oomUnsafe;
 
     if (!target->scriptNameMap) {
       target->scriptNameMap = cx->make_unique<ScriptNameMap>();
 
       if (!target->scriptNameMap) {
         oomUnsafe.crash("Failed to create a script name map.");
       }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -10537,17 +10537,16 @@ static bool SetContextOptions(JSContext*
   if (stopAt >= 0) {
     jit::Simulator::StopSimAt = stopAt;
   }
 #endif
 
   reportWarnings = op.getBoolOption('w');
   compileOnly = op.getBoolOption('c');
   printTiming = op.getBoolOption('b');
-  enableCodeCoverage = op.getBoolOption("code-coverage");
   enableDisassemblyDumps = op.getBoolOption('D');
   cx->runtime()->profilingScripts =
       enableCodeCoverage || enableDisassemblyDumps;
 
 #ifdef DEBUG
   dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
 #endif
 
@@ -11223,16 +11222,21 @@ int main(int argc, char** argv, char** e
     }
   }
 #endif
 
   if (op.getBoolOption("no-threads")) {
     js::DisableExtraThreads();
   }
 
+  enableCodeCoverage = op.getBoolOption("code-coverage");
+  if (enableCodeCoverage) {
+    coverage::EnableLCov();
+  }
+
   AutoLibraryLoader loader;
   MultiStringRange dllPaths = op.getMultiStringOption("dll");
   while (!dllPaths.empty()) {
     char* path = dllPaths.front();
     loader.load(path);
     dllPaths.popFront();
   }
 
--- a/js/src/vm/CodeCoverage.cpp
+++ b/js/src/vm/CodeCoverage.cpp
@@ -594,16 +594,31 @@ bool LCovRealm::writeRealmName(JS::Realm
     outTN_.put("\n", 1);
   } else {
     outTN_.printf("Realm_%p%p\n", (void*)size_t('_'), realm);
   }
 
   return !outTN_.hadOutOfMemory();
 }
 
+bool gLCovIsEnabled = false;
+
+void InitLCov() {
+  const char* outDir = getenv("JS_CODE_COVERAGE_OUTPUT_DIR");
+  if (outDir && *outDir != 0) {
+    EnableLCov();
+  }
+}
+
+void EnableLCov() {
+  MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
+             "EnableLCov must not be called after creating a runtime!");
+  gLCovIsEnabled = true;
+}
+
 LCovRuntime::LCovRuntime() : out_(), pid_(getpid()), isEmpty_(true) {}
 
 LCovRuntime::~LCovRuntime() {
   if (out_.isInitialized()) {
     finishFile();
   }
 }
 
--- a/js/src/vm/CodeCoverage.h
+++ b/js/src/vm/CodeCoverage.h
@@ -120,25 +120,16 @@ class LCovRuntime {
   // directory, create a file inside this directory which uses the process
   // ID, the thread ID and a timestamp to ensure the uniqueness of the
   // file.
   //
   // At the end of the execution, this file should contains the LCOV output of
   // all the scripts executed in the current JSRuntime.
   void init();
 
-  // Check if we should collect code coverage information.
-  bool isEnabled() const {
-    static bool isEnabled_ = ([]() {
-      const char* outDir = getenv("JS_CODE_COVERAGE_OUTPUT_DIR");
-      return outDir && *outDir != 0;
-    })();
-    return isEnabled_;
-  }
-
   // Write the aggregated result of the code coverage of a realm
   // into a file.
   void writeLCovResult(LCovRealm& realm);
 
  private:
   // When a process forks, the file will remain open, but 2 processes will
   // have the same file. To avoid conflicting writes, we open a new file for
   // the child process.
@@ -161,12 +152,21 @@ class LCovRuntime {
   uint32_t pid_;
 
   // Flag used to report if the generated file is empty or not. If it is empty
   // when the runtime is destroyed, then the file would be removed as an empty
   // file is not a valid LCov file.
   bool isEmpty_;
 };
 
+extern void InitLCov();
+
+extern void EnableLCov();
+
+inline bool IsLCovEnabled() {
+  extern bool gLCovIsEnabled;
+  return gLCovIsEnabled;
+}
+
 }  // namespace coverage
 }  // namespace js
 
 #endif  // vm_Printer_h
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -109,16 +109,18 @@ JS_PUBLIC_API const char* JS::detail::In
   js::InitMallocAllocator();
 
   RETURN_IF_FAIL(js::Mutex::Init());
 
   RETURN_IF_FAIL(js::wasm::Init());
 
   js::gc::InitMemorySubsystem();  // Ensure gc::SystemPageSize() works.
 
+  js::coverage::InitLCov();
+
   RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory());
 
   RETURN_IF_FAIL(js::MemoryProtectionExceptionHandler::install());
 
   RETURN_IF_FAIL(js::jit::InitializeIon());
 
   RETURN_IF_FAIL(js::InitDateTimeState());
 
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -282,17 +282,16 @@ struct JSContext : public JS::RootingCon
   size_t gcSystemPageSize() { return js::gc::SystemPageSize(); }
   bool jitSupportsFloatingPoint() const {
     return runtime_->jitSupportsFloatingPoint;
   }
   bool jitSupportsUnalignedAccesses() const {
     return runtime_->jitSupportsUnalignedAccesses;
   }
   bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
-  bool lcovEnabled() const { return runtime_->lcovOutput().isEnabled(); }
 
   /*
    * "Entering" a realm changes cx->realm (which changes cx->global). Note
    * that this does not push an Activation so it's possible for the caller's
    * realm to be != cx->realm(). This is not a problem since, in general, most
    * places in the VM cannot know that they were called from script (e.g.,
    * they may have been called through the JSAPI via JS_CallFunction) and thus
    * cannot expect there is a scripted caller.
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -3356,17 +3356,17 @@ JSScript* JSScript::Create(JSContext* cx
   script->setFlag(ImmutableFlags::SelfHosted, options.selfHostingMode);
   script->setFlag(ImmutableFlags::TreatAsRunOnce, options.isRunOnce);
   script->setFlag(MutableFlags::HideScriptFromDebugger,
                   options.hideScriptFromDebugger);
 
   script->setFlag(MutableFlags::TrackRecordReplayProgress,
                   ShouldTrackRecordReplayProgress(script));
 
-  if (cx->runtime()->lcovOutput().isEnabled()) {
+  if (coverage::IsLCovEnabled()) {
     if (!script->initScriptName(cx)) {
       return nullptr;
     }
   }
 
   return script;
 }
 
@@ -3709,18 +3709,18 @@ js::GlobalObject& JSScript::uninlinedGlo
 void JSScript::finalize(FreeOp* fop) {
   // NOTE: this JSScript may be partially initialized at this point.  E.g. we
   // may have created it and partially initialized it with
   // JSScript::Create(), but not yet finished initializing it with
   // fullyInitFromEmitter() or fullyInitTrivial().
 
   // Collect code coverage information for this script and all its inner
   // scripts, and store the aggregated information on the realm.
-  MOZ_ASSERT_IF(hasScriptName(), fop->runtime()->lcovOutput().isEnabled());
-  if (fop->runtime()->lcovOutput().isEnabled() && hasScriptName()) {
+  MOZ_ASSERT_IF(hasScriptName(), coverage::IsLCovEnabled());
+  if (coverage::IsLCovEnabled() && hasScriptName()) {
     realm()->lcovOutput.collectCodeCoverageInfo(realm(), this, getScriptName());
     destroyScriptName();
   }
 
   fop->runtime()->geckoProfiler().onScriptFinalized(this);
 
   if (types_) {
     types_->destroy(zone());
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -57,19 +57,18 @@ Realm::Realm(Compartment* comp, const JS
 
   runtime_->numRealms++;
 }
 
 Realm::~Realm() {
   MOZ_ASSERT(!hasBeenEnteredIgnoringJit());
 
   // Write the code coverage information in a file.
-  JSRuntime* rt = runtimeFromMainThread();
-  if (rt->lcovOutput().isEnabled()) {
-    rt->lcovOutput().writeLCovResult(lcovOutput);
+  if (coverage::IsLCovEnabled()) {
+    runtime_->lcovOutput().writeLCovResult(lcovOutput);
   }
 
   MOZ_ASSERT(runtime_->numRealms > 0);
   runtime_->numRealms--;
 }
 
 bool ObjectRealm::init(JSContext* cx) {
   NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx));
@@ -821,19 +820,17 @@ bool Realm::collectCoverage() const {
   return collectCoverageForPGO() || collectCoverageForDebug();
 }
 
 bool Realm::collectCoverageForPGO() const {
   return !jit::JitOptions.disablePgo;
 }
 
 bool Realm::collectCoverageForDebug() const {
-  return debuggerObservesCoverage() ||
-         runtimeFromAnyThread()->profilingScripts ||
-         runtimeFromAnyThread()->lcovOutput().isEnabled();
+  return debuggerObservesCoverage() || coverage::IsLCovEnabled();
 }
 
 void Realm::clearScriptCounts() {
   if (!scriptCountsMap) {
     return;
   }
 
   // Clear all hasScriptCounts_ flags of JSScript, in order to release all