Bug 1647791 - WebAssembly: add telemetry for duplicate imports r=lth
authorLuke Wagner <mail@lukewagner.name>
Thu, 07 Jan 2021 23:50:38 +0000
changeset 563234 17d5cf143c0c22b388e326b636231dcbd674fe65
parent 563233 f57b0da990b0ce2ebd79907efa7ce41a40ba8aa1
child 563235 556c25fdbc5ac8fd4dd3c1e6d82597fd5d0f723b
push id38107
push userapavel@mozilla.com
push dateFri, 15 Jan 2021 13:56:45 +0000
treeherdermozilla-central@981e9741e62c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1647791
milestone86.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 1647791 - WebAssembly: add telemetry for duplicate imports r=lth Differential Revision: https://phabricator.services.mozilla.com/D100555
dom/base/UseCounters.conf
js/public/friend/UsageStatistics.h
js/src/wasm/WasmCode.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmValidate.cpp
js/src/wasm/WasmValidate.h
js/xpconnect/src/XPCJSRuntime.cpp
--- a/dom/base/UseCounters.conf
+++ b/dom/base/UseCounters.conf
@@ -76,16 +76,17 @@ attribute DataTransfer.mozSourceNode
 // Marquee events
 custom onstart sets a <marquee> onstart event listener
 custom onbounce sets a <marquee> onbounce event listener
 custom onfinish sets a <marquee> onfinish event listener
 
 // JavaScript feature usage
 custom JS_asmjs uses asm.js
 custom JS_wasm uses WebAssembly
+custom JS_wasm_duplicate_imports uses duplicate imports in WebAssembly
 
 // Console API
 method console.assert
 method console.clear
 method console.count
 method console.countReset
 method console.debug
 method console.error
--- a/js/public/friend/UsageStatistics.h
+++ b/js/public/friend/UsageStatistics.h
@@ -78,16 +78,16 @@ extern JS_FRIEND_API void JS_SetAccumula
 
 /*
  * Use counter names passed to the accumulate use counter callback.
  *
  * It's OK to for these enum values to change as they will be mapped to a
  * fixed member of the mozilla::UseCounter enum by the callback.
  */
 
-enum class JSUseCounter { ASMJS, WASM };
+enum class JSUseCounter { ASMJS, WASM, WASM_DUPLICATE_IMPORTS };
 
 using JSSetUseCounterCallback = void (*)(JSObject*, JSUseCounter);
 
 extern JS_FRIEND_API void JS_SetSetUseCounterCallback(
     JSContext* cx, JSSetUseCounterCallback callback);
 
 #endif  // js_friend_UsageStatistics_h
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -325,25 +325,27 @@ struct MetadataCacheablePod {
   uint64_t minMemoryLength;
   uint32_t globalDataLength;
   Maybe<uint64_t> maxMemoryLength;
   Maybe<uint32_t> startFuncIndex;
   Maybe<uint32_t> nameCustomSectionIndex;
   bool filenameIsURL;
   bool v128Enabled;
   bool omitsBoundsChecks;
+  bool usesDuplicateImports;
 
   explicit MetadataCacheablePod(ModuleKind kind)
       : kind(kind),
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0),
         globalDataLength(0),
         filenameIsURL(false),
         v128Enabled(false),
-        omitsBoundsChecks(false) {}
+        omitsBoundsChecks(false),
+        usesDuplicateImports(false) {}
 };
 
 typedef uint8_t ModuleHash[8];
 typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
 typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncReturnTypesVector;
 
 struct Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod {
   TypeDefWithIdVector types;
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1153,16 +1153,17 @@ SharedMetadata ModuleGenerator::finishMe
 #ifdef ENABLE_WASM_EXCEPTIONS
   metadata_->events = std::move(moduleEnv_->events);
 #endif
   metadata_->nameCustomSectionIndex = moduleEnv_->nameCustomSectionIndex;
   metadata_->moduleName = moduleEnv_->moduleName;
   metadata_->funcNames = std::move(moduleEnv_->funcNames);
   metadata_->omitsBoundsChecks = moduleEnv_->hugeMemoryEnabled();
   metadata_->v128Enabled = moduleEnv_->v128Enabled();
+  metadata_->usesDuplicateImports = moduleEnv_->usesDuplicateImports;
 
   // Copy over additional debug information.
 
   if (compilerEnv_->debugEnabled()) {
     metadata_->debugEnabled = true;
 
     const size_t numFuncs = moduleEnv_->funcs.length();
     if (!metadata_->debugFuncArgTypes.resize(numFuncs)) {
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -1342,14 +1342,19 @@ bool Module::instantiate(JSContext* cx, 
       return false;
     }
   }
 
   JSUseCounter useCounter =
       metadata().isAsmJS() ? JSUseCounter::ASMJS : JSUseCounter::WASM;
   cx->runtime()->setUseCounter(instance, useCounter);
 
+  if (metadata().usesDuplicateImports) {
+    cx->runtime()->setUseCounter(instance,
+                                 JSUseCounter::WASM_DUPLICATE_IMPORTS);
+  }
+
   if (cx->options().testWasmAwaitTier2()) {
     testingBlockOnTier2Complete();
   }
 
   return true;
 }
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -2042,27 +2042,59 @@ static bool DecodeEvent(Decoder& d, Modu
   }
   if (env->types[*funcTypeIndex].funcType().results().length() != 0) {
     return d.fail("exception function types must not return anything");
   }
   return true;
 }
 #endif
 
-static bool DecodeImport(Decoder& d, ModuleEnvironment* env) {
+struct CStringPair {
+  const char* first;
+  const char* second;
+
+  CStringPair(const char* first, const char* second)
+      : first(first), second(second) {}
+
+  using Key = CStringPair;
+  using Lookup = CStringPair;
+
+  static mozilla::HashNumber hash(const Lookup& l) {
+    return mozilla::AddToHash(mozilla::HashString(l.first),
+                              mozilla::HashString(l.second));
+  }
+  static bool match(const Key& k, const Lookup& l) {
+    return !strcmp(k.first, l.first) && !strcmp(k.second, l.second);
+  }
+};
+
+using CStringPairSet = HashSet<CStringPair, CStringPair, SystemAllocPolicy>;
+
+static bool DecodeImport(Decoder& d, ModuleEnvironment* env,
+                         CStringPairSet* dupSet) {
   UniqueChars moduleName = DecodeName(d);
   if (!moduleName) {
     return d.fail("expected valid import module name");
   }
 
   UniqueChars funcName = DecodeName(d);
   if (!funcName) {
     return d.fail("expected valid import func name");
   }
 
+  // It is valid to store raw pointers in dupSet because moduleName and funcName
+  // become owned by env->imports on all non-error paths, outliving dupSet.
+  CStringPair pair(moduleName.get(), funcName.get());
+  CStringPairSet::AddPtr p = dupSet->lookupForAdd(pair);
+  if (p) {
+    env->usesDuplicateImports = true;
+  } else if (!dupSet->add(p, pair)) {
+    return false;
+  }
+
   uint8_t rawImportKind;
   if (!d.readFixedU8(&rawImportKind)) {
     return d.fail("failed to read import kind");
   }
 
   DefinitionKind importKind = DefinitionKind(rawImportKind);
 
   switch (importKind) {
@@ -2165,18 +2197,19 @@ static bool DecodeImportSection(Decoder&
   if (!d.readVarU32(&numImports)) {
     return d.fail("failed to read number of imports");
   }
 
   if (numImports > MaxImports) {
     return d.fail("too many imports");
   }
 
+  CStringPairSet dupSet;
   for (uint32_t i = 0; i < numImports; i++) {
-    if (!DecodeImport(d, env)) {
+    if (!DecodeImport(d, env, &dupSet)) {
       return false;
     }
   }
 
   if (!d.finishSection(*range, "import")) {
     return false;
   }
 
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -147,31 +147,33 @@ struct ModuleEnvironment {
   TableDescVector tables;
   Uint32Vector asmJSSigToTableIndex;
   ImportVector imports;
   ExportVector exports;
   Maybe<uint32_t> startFuncIndex;
   ElemSegmentVector elemSegments;
   MaybeSectionRange codeSection;
   SparseBitmap validForRefFunc;
+  bool usesDuplicateImports;
 
   // Fields decoded as part of the wasm module tail:
   DataSegmentEnvVector dataSegments;
   CustomSectionEnvVector customSections;
   Maybe<uint32_t> nameCustomSectionIndex;
   Maybe<Name> moduleName;
   NameVector funcNames;
 
   explicit ModuleEnvironment(FeatureArgs features,
                              ModuleKind kind = ModuleKind::Wasm)
       : kind(kind),
         features(features),
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0),
-        types(features, TypeDefVector()) {}
+        types(features, TypeDefVector()),
+        usesDuplicateImports(false) {}
 
   size_t numTables() const { return tables.length(); }
   size_t numTypes() const { return types.length(); }
   size_t numFuncs() const { return funcs.length(); }
   size_t numFuncImports() const { return funcImportGlobalDataOffsets.length(); }
   size_t numFuncDefs() const {
     return funcs.length() - funcImportGlobalDataOffsets.length();
   }
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2647,16 +2647,19 @@ static void AccumulateTelemetryCallback(
 static void SetUseCounterCallback(JSObject* obj, JSUseCounter counter) {
   switch (counter) {
     case JSUseCounter::ASMJS:
       SetUseCounter(obj, eUseCounter_custom_JS_asmjs);
       break;
     case JSUseCounter::WASM:
       SetUseCounter(obj, eUseCounter_custom_JS_wasm);
       break;
+    case JSUseCounter::WASM_DUPLICATE_IMPORTS:
+      SetUseCounter(obj, eUseCounter_custom_JS_wasm_duplicate_imports);
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unexpected JSUseCounter id");
   }
 }
 
 static void GetRealmNameCallback(JSContext* cx, Realm* realm, char* buf,
                                  size_t bufsize,
                                  const JS::AutoRequireNoGC& nogc) {