diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 2bf6aa1c387..16758479472 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -8316,9 +8316,6 @@ Parser::accumulateTelemetry() JSContext* cx = context->maybeJSContext(); if (!cx) return; - JSAccumulateTelemetryDataCallback cb = cx->runtime()->telemetryCallback; - if (!cb) - return; const char* filename = getFilename(); if (!filename) return; @@ -8342,13 +8339,13 @@ Parser::accumulateTelemetry() // Call back into Firefox's Telemetry reporter. if (sawDeprecatedForEach) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedForEach); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedForEach); if (sawDeprecatedDestructuringForIn) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedDestructuringForIn); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedDestructuringForIn); if (sawDeprecatedLegacyGenerator) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLegacyGenerator); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLegacyGenerator); if (sawDeprecatedExpressionClosure) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedExpressionClosure); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedExpressionClosure); } template class Parser; diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index b1b630bd3d8..55e1784ff67 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -742,28 +742,26 @@ Statistics::endGC() for (int i = 0; i < PHASE_LIMIT; i++) phaseTotals[i] += phaseTimes[i]; - if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { - int64_t total, longest; - gcDuration(&total, &longest); + int64_t total, longest; + gcDuration(&total, &longest); - int64_t sccTotal, sccLongest; - sccDurations(&sccTotal, &sccLongest); + int64_t sccTotal, sccLongest; + sccDurations(&sccTotal, &sccLongest); - (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, !zoneStats.isCollectingAllZones()); - (*cb)(JS_TELEMETRY_GC_MS, t(total)); - (*cb)(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest)); - (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK])); - (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP])); - (*cb)(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(phaseTimes[PHASE_MARK_ROOTS])); - (*cb)(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_SWEEP_MARK_GRAY])); - (*cb)(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason); - (*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed()); - (*cb)(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal)); - (*cb)(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest)); + runtime->addTelemetry(JS_TELEMETRY_GC_IS_COMPARTMENTAL, !zoneStats.isCollectingAllZones()); + runtime->addTelemetry(JS_TELEMETRY_GC_MS, t(total)); + runtime->addTelemetry(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest)); + runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK])); + runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP])); + runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(phaseTimes[PHASE_MARK_ROOTS])); + runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_SWEEP_MARK_GRAY])); + runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason); + runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed()); + runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal)); + runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest)); - double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); - (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100); - } + double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); + runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100); if (fp) printStats(); @@ -783,8 +781,7 @@ Statistics::beginSlice(const ZoneGCStats &zoneStats, JSGCInvocationKind gckind, if (!slices.append(data)) CrashAtUnhandlableOOM("Failed to allocate statistics slice."); - if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) - (*cb)(JS_TELEMETRY_GC_REASON, reason); + runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason); // Slice callbacks should only fire for the outermost level if (++gcDepth == 1) { @@ -801,10 +798,8 @@ Statistics::endSlice() slices.back().end = PRMJ_Now(); slices.back().endFaults = GetPageFaultCount(); - if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { - (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start)); - (*cb)(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason); - } + runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start)); + runtime->addTelemetry(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason); bool last = runtime->gc.state() == gc::NO_INCREMENTAL; if (last) diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index c6ea97105fe..060d29b72af 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -713,8 +713,23 @@ ErrorReport::init(JSContext *cx, HandleValue exn) if (exn.isObject()) { exnObject = &exn.toObject(); reportp = js_ErrorFromException(cx, exnObject); - } + JSCompartment *comp = exnObject->compartment(); + JSAddonId *addonId = comp->addonId; + if (addonId) { + UniqueChars addonIdChars(JS_EncodeString(cx, addonId)); + + const char *filename = strrchr(reportp->filename, '/'); + if (filename) + filename++; + else + filename = "COULD_NOT_FIND_FILENAME"; + + char histogramKey[64]; + JS_snprintf(histogramKey, sizeof(histogramKey), "%s %s %u", addonIdChars, filename, reportp->lineno); + cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey); + } + } // Be careful not to invoke ToString if we've already successfully extracted // an error report, since the exception might be wrapped in a security // wrapper, and ToString-ing it might throw. diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index c86b0344786..6935536a004 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -681,7 +681,7 @@ js::StringToLinearStringSlow(JSContext *cx, JSString *str) JS_FRIEND_API(void) JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback) { - rt->telemetryCallback = callback; + rt->setTelemetryCallback(rt, callback); } JS_FRIEND_API(JSObject *) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 78112e6e3dc..e9a7a0ff67e 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -110,11 +110,12 @@ enum { JS_TELEMETRY_GC_NON_INCREMENTAL, JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, - JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT + JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, + JS_TELEMETRY_ADDON_EXCEPTIONS }; typedef void -(* JSAccumulateTelemetryDataCallback)(int id, uint32_t sample); +(*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char *key); extern JS_FRIEND_API(void) JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index fa1167bb386..33c7285481b 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -458,6 +458,19 @@ JSRuntime::~JSRuntime() #endif } +void +JSRuntime::addTelemetry(int id, uint32_t sample, const char *key) +{ + if (telemetryCallback) + (*telemetryCallback)(id, sample, key); +} + +void +JSRuntime::setTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback) +{ + rt->telemetryCallback = callback; +} + void NewObjectCache::clearNurseryObjects(JSRuntime *rt) { diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index f10fbbce661..be1fbdd385e 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -702,7 +702,16 @@ struct JSRuntime : public JS::shadow::Runtime, private: mozilla::Atomic interrupt_; mozilla::Atomic interruptPar_; + + /* Call this to accumulate telemetry data. */ + JSAccumulateTelemetryDataCallback telemetryCallback; public: + // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_* + // histogram. |key| provides an additional key to identify the histogram. + // |sample| is the data to add to the histogram. + void addTelemetry(int id, uint32_t sample, const char *key = nullptr); + + void setTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback); enum InterruptMode { RequestInterruptUrgent, @@ -1085,9 +1094,6 @@ struct JSRuntime : public JS::shadow::Runtime, /* Structured data callbacks are runtime-wide. */ const JSStructuredCloneCallbacks *structuredCloneCallbacks; - /* Call this to accumulate telemetry data. */ - JSAccumulateTelemetryDataCallback telemetryCallback; - /* Optional error reporter. */ JSErrorReporter errorReporter; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 9554aa2ee05..b4bbc92ba4d 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2963,7 +2963,7 @@ DiagnosticMemoryCallback(void *ptr, size_t size) #endif static void -AccumulateTelemetryCallback(int id, uint32_t sample) +AccumulateTelemetryCallback(int id, uint32_t sample, const char *key) { switch (id) { case JS_TELEMETRY_GC_REASON: @@ -3015,6 +3015,9 @@ AccumulateTelemetryCallback(int id, uint32_t sample) MOZ_ASSERT(sample <= 3); Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, sample); break; + case JS_TELEMETRY_ADDON_EXCEPTIONS: + Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample); + break; default: MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id"); } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 9f049e28a17..30167a1f658 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -324,6 +324,12 @@ "n_values": 10, "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach, DestructuringForIn, LegacyGenerator, ExpressionClosure" }, + "JS_TELEMETRY_ADDON_EXCEPTIONS" : { + "expires_in_version" : "never", + "kind": "count", + "keyed" : true, + "description" : "Exceptions thrown by add-ons" + }, "TELEMETRY_PING": { "expires_in_version": "default", "kind": "exponential",