Bug 1343483 - Report how long lazy code remains lazy draft
authorDavid Teller <dteller@mozilla.com>
Wed, 01 Mar 2017 22:49:28 +0100
changeset 493183 566bd656151cbafee529cc245b8a56ad61c8dd24
parent 488720 869ee93548d3f1cd9cb437b14c26de4bfdd80806
child 547779 4a1ac9e301665220366a3ad8ba1fa92be2cc7193
push id47669
push userdteller@mozilla.com
push dateFri, 03 Mar 2017 13:44:50 +0000
bugs1343483
milestone54.0a1
Bug 1343483 - Report how long lazy code remains lazy MozReview-Commit-ID: BThGS1eUkp7
js/src/frontend/BytecodeCompiler.cpp
js/src/jsfriendapi.h
js/src/jsscript.h
js/xpconnect/src/XPCJSContext.cpp
toolkit/components/telemetry/Histograms.json
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -336,31 +336,36 @@ BytecodeCompiler::compileScript(HandleOb
                 if (!deoptimizeArgumentsInEnclosingScripts(cx, environment))
                     return nullptr;
             }
             if (!emitter->emitScript(pn))
                 return nullptr;
             if (!NameFunctions(cx, pn))
                 return nullptr;
             parser->handler.freeTree(pn);
-
             break;
         }
 
         // Maybe we aborted a syntax parse. See if we can try again.
         if (!handleParseFailure(directives))
             return nullptr;
 
         // Reset UsedNameTracker state before trying again.
         usedNames->reset();
     }
 
     if (!maybeCompleteCompressSource())
         return nullptr;
 
+    // FIXME: I'm not sure at all that this happens in the right thread.
+    if (!syntaxParser) { // FIXME: Is this the right way to check that we're doing a syntax parse?
+        js::ScriptSource* source = script->scriptSource();
+        source->syntaxParseEnds();
+    }
+
     MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
 
     return script;
 }
 
 JSScript*
 BytecodeCompiler::compileGlobalScript(ScopeKind scopeKind)
 {
@@ -625,16 +630,25 @@ frontend::CompileLazyFunction(JSContext*
 
     CompileOptions options(cx, lazy->version());
     options.setMutedErrors(lazy->mutedErrors())
            .setFileAndLine(lazy->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
+    // Update statistics to find out if we have delazified just after having
+    // lazified.
+    if (!lazy->scriptSource()->syntaxParseEnded().IsNull()) {
+        mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - lazy->scriptSource()->syntaxParseEnded();
+        cx->runtime()->addTelemetry(JS_TELEMETRY_PARSER_COMPILE_LAZY_AFTER_MS,
+          delta.ToMilliseconds()
+        );
+    }
+
     AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileLazy, options);
 
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
     Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
                                     /* foldConstants = */ true, usedNames, nullptr, lazy);
     if (!parser.checkOptions())
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -131,16 +131,17 @@ enum {
     JS_TELEMETRY_GC_MINOR_REASON_LONG,
     JS_TELEMETRY_GC_MINOR_US,
     JS_TELEMETRY_GC_NURSERY_BYTES,
     JS_TELEMETRY_GC_PRETENURE_COUNT,
     JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT,
     JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS,
     JS_TELEMETRY_ADDON_EXCEPTIONS,
     JS_TELEMETRY_AOT_USAGE,
+    JS_TELEMETRY_PARSER_COMPILE_LAZY_AFTER_MS,
     JS_TELEMETRY_END
 };
 
 typedef void
 (*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char* key);
 
 extern JS_FRIEND_API(void)
 JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -350,16 +350,17 @@ class UncompressedSourceCache
 
   private:
     void holdEntry(AutoHoldEntry& holder, const ScriptSourceChunk& ssc);
     void releaseEntry(AutoHoldEntry& holder);
 };
 
 class ScriptSource
 {
+  private:
     friend struct SourceCompressionTask;
 
     uint32_t refs;
 
     // Note: while ScriptSources may be compressed off thread, they are only
     // modified by the active thread, and all members are always safe to access
     // on the active thread.
 
@@ -441,16 +442,24 @@ class ScriptSource
     // demand. If sourceRetrievable_ and hasSourceData() are false, it is not
     // possible to get source at all.
     bool sourceRetrievable_:1;
     bool hasIntroductionOffset_:1;
 
     const char16_t* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
                                size_t chunk);
 
+    // Instant at which the syntax parse of this source ended, or null
+    // if there was no syntax parse.
+    //
+    // Used for statistics purposes, to determine how much time code spends
+    // syntax parsed before being full parsed, to help determine whether
+    // our syntax parse vs. full parse heuristics are correct.
+    mozilla::TimeStamp syntaxParseEnded_;
+
   public:
     explicit ScriptSource()
       : refs(0),
         data(SourceType(Missing())),
         filename_(nullptr),
         displayURL_(nullptr),
         sourceMapURL_(nullptr),
         mutedErrors_(false),
@@ -603,16 +612,24 @@ class ScriptSource
     // ScriptSource.
     bool xdrEncodeFunction(JSContext* cx, HandleFunction fun,
                            HandleScriptSource sourceObject);
 
     // Linearize the encoded content in the |buffer| provided as argument to
     // |xdrEncodeTopLevel|, and free the XDR encoder.  In case of errors, the
     // |buffer| is considered undefined.
     bool xdrFinalizeEncoder();
+
+    const mozilla::TimeStamp syntaxParseEnded() const {
+        return syntaxParseEnded_;
+    }
+    void syntaxParseEnds() {
+        MOZ_ASSERT(syntaxParseEnded_.IsNull());
+        syntaxParseEnded_ = mozilla::TimeStamp::Now();
+    }
 };
 
 class ScriptSourceHolder
 {
     ScriptSource* ss;
   public:
     ScriptSourceHolder()
       : ss(nullptr)
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -3118,16 +3118,19 @@ AccumulateTelemetryCallback(int id, uint
         Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, sample);
         break;
       case JS_TELEMETRY_ADDON_EXCEPTIONS:
         Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample);
         break;
       case JS_TELEMETRY_AOT_USAGE:
         Telemetry::Accumulate(Telemetry::JS_AOT_USAGE, sample);
         break;
+      case JS_TELEMETRY_PARSER_COMPILE_LAZY_AFTER_MS:
+        Telemetry::Accumulate(Telemetry::JS_PARSER_COMPILE_LAZY_AFTER_MS, sample);
+        break;
       default:
         MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id");
     }
 }
 
 static void
 CompartmentNameCallback(JSContext* cx, JSCompartment* comp,
                         char* buf, size_t bufsize)
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1045,16 +1045,26 @@
   },
   "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS": {
     "alert_emails": ["jdemooij@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
     "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10"
   },
+  "JS_PARSER_COMPILE_LAZY_AFTER_MS": {
+    "alert_emails": ["dteller@mozilla.com"],
+    "expires_in_version": "70",
+    "bug_numbers": [1343483],
+    "kind": "exponential",
+    "low": 10,
+    "high": 10000,
+    "n_buckets": 10,
+    "description": "Time elapsed between the moment a function is lazy-parsed (end of parsing of the ScriptSource) and the moment it is recompiled as non-lazy (start of compilation)."
+  },
   "XUL_CACHE_DISABLED": {
     "expires_in_version": "default",
     "kind": "flag",
     "description": "XUL cache was disabled"
   },
   "MEMORY_RESIDENT_FAST": {
     "alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],
     "expires_in_version": "never",