Bug 1202706 - Part 1: Add telemetry histograms for worker use counters; r=chutten,bzbarsky
authorEdgar Chen <echen@mozilla.com>
Wed, 27 Nov 2019 23:22:29 +0000
changeset 505564 4989bb2f4f69eae11fa05cc9386edf3d65ccb0c6
parent 505563 7e3751388d71a1db2ca7e2227c8bb4455dcd7620
child 505565 9eeb03a503fb86df387049e878c0c5dd76e0387c
push id102347
push userechen@mozilla.com
push dateWed, 04 Dec 2019 23:23:46 +0000
treeherderautoland@2dfc53add3de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschutten, bzbarsky
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 1202706 - Part 1: Add telemetry histograms for worker use counters; r=chutten,bzbarsky This patch uses similar architecture of use counters of main thread for workers. Which introduces the following format histograms for use counters of workers USE_COUNTER2_*_DEDICATED_WORKER USE_COUNTER2_*_SHARED_WORKER USE_COUNTER2_*_SERVICE_WORKER And add the following histograms used in conjunction with above use counter histograms DEDICATED_WORKER_DESTROYED SHARED_WORKER_DESTROYED SERVICE_WORKER_DESTROYED Differential Revision: https://phabricator.services.mozilla.com/D53221
copy from dom/base/UseCounters.conf
copy to dom/base/UseCountersWorker.conf
--- a/dom/base/UseCounters.conf
+++ b/dom/base/UseCountersWorker.conf
@@ -13,81 +13,38 @@
 //   (b) a comment, which is a line that begins with "//"
 //   (c) one of four possible use counter declarations:
 //         method <IDL interface name>.<IDL operation name>
 //         attribute <IDL interface name>.<IDL attribute name>
 //         custom <any valid identifier> <description>
-// The <description> for custom counters will be appended to "Whether a document "
-// or "Whether a page ", so phrase it appropriately.  For instance, "constructs a
-// Foo object" or "calls Document.bar('some value')".  It may contain any
-// character (including whitespace).
+// The <description> for custom counters will be appended to "Whether a
+// dedicated worker " or "Whether a shared worker" or "Whether a service worker
+//  ", so phrase it appropriately.  For instance, "constructs a
+// Foo object" or "calls Bar.baz('some value')". It may contain any character
+// (including whitespace).
 // To actually cause use counters to be incremented, DOM methods
-// and attributes must have a [UseCounter] extended attribute in
-// the Web IDL file.
+// and attributes must have a [UseCounter] extended attribute and be exposed to
+// workers in the Web IDL file.
 // Custom counters are incremented when
-// SetUseCounter(eUseCounter_custom_MyName) is called on a Document object.
+// SetUseCounter(UseCounterWoker::Custom_MyName) is called on a WorkerPrivate
+// object.
 // You might reasonably ask why we have this file and we require
 // annotating things with [UseCounter] in the relevant WebIDL file as
 // well.  Generating things from bindings codegen and ensuring all the
 // dependencies were correct would have been rather difficult.
-method SVGSVGElement.getElementById
-attribute SVGSVGElement.currentScale
 // Push API
 method PushManager.subscribe
 method PushSubscription.unsubscribe
-// window.sidebar
-attribute Window.sidebar
-// AppCache API
-method OfflineResourceList.swapCache
-method OfflineResourceList.update
-attribute OfflineResourceList.status
-attribute OfflineResourceList.onchecking
-attribute OfflineResourceList.onerror
-attribute OfflineResourceList.onnoupdate
-attribute OfflineResourceList.ondownloading
-attribute OfflineResourceList.onprogress
-attribute OfflineResourceList.onupdateready
-attribute OfflineResourceList.oncached
-attribute OfflineResourceList.onobsolete
-// Non-standard IndexedDB API
-method IDBDatabase.createMutableFile
-method IDBDatabase.mozCreateFileHandle
-method IDBMutableFile.open
-method IDBMutableFile.getFile
-// DataTransfer API (gecko-only methods)
-method DataTransfer.addElement
-attribute DataTransfer.mozItemCount
-attribute DataTransfer.mozCursor
-method DataTransfer.mozTypesAt
-method DataTransfer.mozClearDataAt
-method DataTransfer.mozSetDataAt
-method DataTransfer.mozGetDataAt
-attribute DataTransfer.mozUserCancelled
-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
 // Console API
 method console.assert
 method console.clear
 method console.count
 method console.countReset
 method console.debug
 method console.error
 method console.info
@@ -103,242 +60,8 @@ method console.groupEnd
 method console.time
 method console.timeLog
 method console.timeEnd
 method console.exception
 method console.timeStamp
 method console.profile
 method console.profileEnd
-// document.open information
-custom DocumentOpen calls document.open in a way that creates a new Window object
-// HTMLDocument named getter
-custom HTMLDocumentNamedGetterHit calls to the named getter on HTMLDocument that find something via the name lookup
-custom FilteredCrossOriginIFrame cross-origin <iframe> within a CSS/SVG filter
-// Custom Elements
-method CustomElementRegistry.define
-// Shadow DOM
-method Element.attachShadow
-// Media Device Access
-method MediaDevices.enumerateDevices
-custom EnumerateDevicesInsec calls MediaDevices.enumerateDevices from an insecure context
-custom EnumerateDevicesUnfocused calls MediaDevices.enumerateDevices from a unfocused document
-method MediaDevices.getUserMedia
-method Navigator.mozGetUserMedia
-custom GetUserMediaUnfocused calls MediaDevices.getUserMedia from an unfocused document
-custom GetUserMediaInsec calls MediaDevices.getUserMedia from an insecure context
-custom MozGetUserMediaInsec calls Navigator.mozGetUserMedia from an insecure context
-custom GetUserMediaXOrigin calls MediaDevices.getUserMedia from a cross origin context
-custom MozGetUserMediaXOrigin calls Navigator.mozGetUserMedia from a cross origin context
-method MediaDevices.getDisplayMedia
-custom GetDisplayMediaXOrigin calls MediaDevices.getDisplayMedia from a cross origin context
-// Missing-property use counters.  We claim these are "method" use
-// counters, because we don't need a separate description string for
-// them and we only need one use counter, not a getter/setter pair.
-method HTMLDocument.adoptedStyleSheets
-method HTMLDocument.caretRangeFromPoint
-method HTMLDocument.clear
-method HTMLDocument.exitPictureInPicture
-method HTMLDocument.featurePolicy
-method HTMLDocument.onbeforecopy
-method HTMLDocument.onbeforecut
-method HTMLDocument.onbeforepaste
-method HTMLDocument.oncancel
-method HTMLDocument.onfreeze
-method HTMLDocument.onmousewheel
-method HTMLDocument.onresume
-method HTMLDocument.onsearch
-method HTMLDocument.onsecuritypolicyviolation
-method HTMLDocument.onwebkitfullscreenchange
-method HTMLDocument.onwebkitfullscreenerror
-method HTMLDocument.pictureInPictureElement
-method HTMLDocument.pictureInPictureEnabled
-method HTMLDocument.registerElement
-method HTMLDocument.wasDiscarded
-method HTMLDocument.webkitCancelFullScreen
-method HTMLDocument.webkitCurrentFullScreenElement
-method HTMLDocument.webkitExitFullscreen
-method HTMLDocument.webkitFullscreenElement
-method HTMLDocument.webkitFullscreenEnabled
-method HTMLDocument.webkitHidden
-method HTMLDocument.webkitIsFullScreen
-method HTMLDocument.webkitVisibilityState
-method HTMLDocument.xmlEncoding
-method HTMLDocument.xmlStandalone
-method HTMLDocument.xmlVersion
-method Window.AbsoluteOrientationSensor
-method Window.Accelerometer
-method Window.ApplicationCache
-method Window.ApplicationCacheErrorEvent
-method Window.Atomics
-method Window.AudioParamMap
-method Window.AudioWorklet
-method Window.AudioWorkletNode
-method Window.BackgroundFetchManager
-method Window.BackgroundFetchRecord
-method Window.BackgroundFetchRegistration
-method Window.BeforeInstallPromptEvent
-method Window.Bluetooth
-method Window.BluetoothCharacteristicProperties
-method Window.BluetoothDevice
-method Window.BluetoothRemoteGATTCharacteristic
-method Window.BluetoothRemoteGATTDescriptor
-method Window.BluetoothRemoteGATTServer
-method Window.BluetoothRemoteGATTService
-method Window.BluetoothUUID
-method Window.CanvasCaptureMediaStreamTrack
-method Window.chrome
-method Window.clientInformation
-method Window.ClipboardItem
-method Window.CSSImageValue
-method Window.CSSKeywordValue
-method Window.CSSMathInvert
-method Window.CSSMathMax
-method Window.CSSMathMin
-method Window.CSSMathNegate
-method Window.CSSMathProduct
-method Window.CSSMathSum
-method Window.CSSMathValue
-method Window.CSSMatrixComponent
-method Window.CSSNumericArray
-method Window.CSSNumericValue
-method Window.CSSPerspective
-method Window.CSSPositionValue
-method Window.CSSRotate
-method Window.CSSScale
-method Window.CSSSkew
-method Window.CSSSkewX
-method Window.CSSSkewY
-method Window.CSSStyleValue
-method Window.CSSTransformComponent
-method Window.CSSTransformValue
-method Window.CSSTranslate
-method Window.CSSUnitValue
-method Window.CSSUnparsedValue
-method Window.CSSVariableReferenceValue
-method Window.defaultStatus
-// See comments in Window.webidl about why this is disabled.
-//method Window.defaultstatus
-method Window.DeviceMotionEventAcceleration
-method Window.DeviceMotionEventRotationRate
-method Window.DOMError
-method Window.EnterPictureInPictureEvent
-method Window.External
-method Window.FederatedCredential
-method Window.Gyroscope
-method Window.HTMLContentElement
-method Window.HTMLDialogElement
-method Window.HTMLShadowElement
-method Window.ImageCapture
-method Window.InputDeviceCapabilities
-method Window.InputDeviceInfo
-method Window.Keyboard
-method Window.KeyboardLayoutMap
-method Window.LinearAccelerationSensor
-method Window.Lock
-method Window.LockManager
-method Window.MediaMetadata
-method Window.MediaSession
-method Window.MediaSettingsRange
-method Window.MIDIAccess
-method Window.MIDIConnectionEvent
-method Window.MIDIInput
-method Window.MIDIInputMap
-method Window.MIDIMessageEvent
-method Window.MIDIOutput
-method Window.MIDIOutputMap
-method Window.MIDIPort
-method Window.NavigationPreloadManager
-method Window.NetworkInformation
-method Window.offscreenBuffering
-method Window.OffscreenCanvas
-method Window.OffscreenCanvasRenderingContext2D
-method Window.onappinstalled
-method Window.onbeforeinstallprompt
-method Window.oncancel
-method Window.ondeviceorientationabsolute
-method Window.onmousewheel
-method Window.onsearch
-method Window.onselectionchange
-method Window.openDatabase
-method Window.OrientationSensor
-method Window.OverconstrainedError
-method Window.PasswordCredential
-method Window.PaymentAddress
-method Window.PaymentInstruments
-method Window.PaymentManager
-method Window.PaymentMethodChangeEvent
-method Window.PaymentRequest
-method Window.PaymentRequestUpdateEvent
-method Window.PaymentResponse
-method Window.PerformanceEventTiming
-method Window.PerformanceLongTaskTiming
-method Window.PerformancePaintTiming
-method Window.PhotoCapabilities
-method Window.PictureInPictureWindow
-method Window.Presentation
-method Window.PresentationAvailability
-method Window.PresentationConnection
-method Window.PresentationConnectionAvailableEvent
-method Window.PresentationConnectionCloseEvent
-method Window.PresentationConnectionList
-method Window.PresentationReceiver
-method Window.PresentationRequest
-method Window.RelativeOrientationSensor
-method Window.RemotePlayback
-method Window.ReportingObserver
-method Window.RTCDtlsTransport
-method Window.RTCError
-method Window.RTCErrorEvent
-method Window.RTCIceTransport
-method Window.RTCSctpTransport
-method Window.Sensor
-method Window.SensorErrorEvent
-method Window.SharedArrayBuffer
-method Window.styleMedia
-method Window.StylePropertyMap
-method Window.StylePropertyMapReadOnly
-method Window.SVGDiscardElement
-method Window.SyncManager
-method Window.TaskAttributionTiming
-method Window.TextDecoderStream
-method Window.TextEncoderStream
-method Window.TextEvent
-method Window.Touch
-method Window.TouchEvent
-method Window.TouchList
-method Window.TransformStream
-method Window.USB
-method Window.USBAlternateInterface
-method Window.USBConfiguration
-method Window.USBConnectionEvent
-method Window.USBDevice
-method Window.USBEndpoint
-method Window.USBInterface
-method Window.USBInTransferResult
-method Window.USBIsochronousInTransferPacket
-method Window.USBIsochronousInTransferResult
-method Window.USBIsochronousOutTransferPacket
-method Window.USBIsochronousOutTransferResult
-method Window.USBOutTransferResult
-method Window.UserActivation
-method Window.visualViewport
-method Window.webkitCancelAnimationFrame
-method Window.webkitMediaStream
-method Window.WebKitMutationObserver
-method Window.webkitRequestAnimationFrame
-method Window.webkitRequestFileSystem
-method Window.webkitResolveLocalFileSystemURL
-method Window.webkitRTCPeerConnection
-method Window.webkitSpeechGrammar
-method Window.webkitSpeechGrammarList
-method Window.webkitSpeechRecognition
-method Window.webkitSpeechRecognitionError
-method Window.webkitSpeechRecognitionEvent
-method Window.webkitStorageInfo
-method Window.Worklet
-method Window.WritableStream
--- a/dom/base/usecounters.py
+++ b/dom/base/usecounters.py
@@ -38,29 +38,35 @@ def read_conf(conf_filename):
                 yield { 'type': 'custom',
                         'name': name,
                         'desc': desc }
             raise ValueError('error parsing %s at line %d' % (conf_filename, line_num))
     return parse_counters(stream)
-def generate_histograms(filename):
+def generate_histograms(filename, is_for_worker=False):
     # The mapping for use counters to telemetry histograms depends on the
     # ordering of items in the dictionary.
+    # The ordering of the ending for workers depends on the WorkerType defined
+    # in WorkerPrivate.h.
+    endings = ["DEDICATED_WORKER", "SHARED_WORKER", "SERVICE_WORKER"] if is_for_worker else ["DOCUMENT", "PAGE"]
     items = collections.OrderedDict()
     for counter in read_conf(filename):
         def append_counter(name, desc):
             items[name] = { 'expires_in_version': 'never',
                             'kind' : 'boolean',
                             'description': desc }
         def append_counters(name, desc):
-            append_counter('USE_COUNTER2_%s_DOCUMENT' % name, 'Whether a document %s' % desc)
-            append_counter('USE_COUNTER2_%s_PAGE' % name, 'Whether a page %s' % desc)
+            for ending in endings:
+                append_counter('USE_COUNTER2_%s_%s' % (name, ending),
+                               'Whether a %s %s' % (ending.replace('_', ' ').lower(), desc))
         if counter['type'] == 'method':
             method = '%s.%s' % (counter['interface_name'], counter['method_name'])
             append_counters(method.replace('.', '_').upper(), 'called %s' % method)
         elif counter['type'] == 'attribute':
             attr = '%s.%s' % (counter['interface_name'], counter['attribute_name'])
             counter_name = attr.replace('.', '_').upper()
             append_counters('%s_getter' % counter_name, 'got %s' % attr)
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -11982,16 +11982,26 @@
     "high": 2000,
     "n_buckets": 25,
     "keyed": true,
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["sw-telemetry@mozilla.com"],
     "description": "Time (in ms) measured between when the fetch handler finished executing and when we reset the network channel."
+    "record_in_processes": ["main", "content"],
+    "products": ["firefox", "fennec", "geckoview"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1202706],
+    "alert_emails": ["tdsmith@mozilla.com", "compatibility@lists.mozilla.org"],
+    "description": "Number of service workers destroyed; used in conjunction with use counter histograms"
+  },
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "expires_in_version": "45",
     "kind": "enumerated",
     "n_values": 3,
     "description": "What button a user clicked in an onbeforeunload prompt.  (Stay on Page = 0, Leave Page = 1, prompt aborted = 2)"
@@ -12537,25 +12547,45 @@
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": ["amarchesini@mozilla.com"],
     "bug_numbers": [1286895],
     "expires_in_version": "never",
     "kind": "count",
     "description": "Tracking whether a SharedWorker spawn gets queued due to hitting max workers per domain limit. File bugs in Core::DOM in case of a Telemetry regression."
+    "record_in_processes": ["main", "content"],
+    "products": ["firefox", "fennec", "geckoview"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1202706],
+    "alert_emails": ["tdsmith@mozilla.com", "compatibility@lists.mozilla.org"],
+    "description": "Number of shared workers destroyed; used in conjunction with use counter histograms"
+  },
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": ["amarchesini@mozilla.com"],
     "bug_numbers": [1286895],
     "expires_in_version": "never",
     "kind": "count",
     "description": "Tracking whether a DedicatedWorker spawn gets queued due to hitting max workers per domain limit. File bugs in Core::DOM in case of a Telemetry regression."
+    "record_in_processes": ["main", "content"],
+    "products": ["firefox", "fennec", "geckoview"],
+    "expires_in_version": "never",
+    "kind": "count",
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1202706],
+    "alert_emails": ["tdsmith@mozilla.com", "compatibility@lists.mozilla.org"],
+    "description": "Number of dedicated workers destroyed; used in conjunction with use counter histograms"
+  },
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "expires_in_version": "50",
     "kind": "count",
     "description": "Count how many registrations occurs. File bugs in Core::DOM in case of a Telemetry regression."
--- a/toolkit/components/telemetry/build_scripts/gen_histogram_enum.py
+++ b/toolkit/components/telemetry/build_scripts/gen_histogram_enum.py
@@ -35,64 +35,75 @@ namespace Telemetry {
 footer = """
 } // namespace mozilla
 } // namespace Telemetry
 #endif // mozilla_TelemetryHistogramEnums_h"""
+def get_histogram_typename(histogram):
+    name = histogram.name()
+    if name.startswith("USE_COUNTER2_"):
+        return "UseCounterWorker" if name.endswith("_WORKER") else "UseCounter"
+    return None
 def main(output, *filenames):
     # Print header.
     print(banner, file=output)
     print(header, file=output)
     # Load the histograms.
         all_histograms = list(parse_histograms.from_files(filenames))
     except ParserError as ex:
         print("\nError processing histograms:\n" + str(ex) + "\n")
-    groups = itertools.groupby(all_histograms,
-                               lambda h: h.name().startswith("USE_COUNTER2_"))
+    groups = itertools.groupby(all_histograms, get_histogram_typename)
     # Print the histogram enums.
-    # Note that parse_histograms.py guarantees that all of the USE_COUNTER2_*
-    # histograms are defined in a contiguous block.  We therefore assume
-    # that there's at most one group for which use_counter_group is true.
+    # Note that parse_histograms.py guarantees that all of the
+    # USE_COUNTER2_*_WORKER and USE_COUNTER2_* histograms are both defined in a
+    # contiguous block.
     print("enum HistogramID : uint32_t {", file=output)
-    seen_use_counters = False
-    for (use_counter_group, histograms) in groups:
-        if use_counter_group:
-            seen_use_counters = True
-        # The HistogramDUMMY* enum variables are used to make the computation
-        # of Histogram{First,Last}UseCounter easier.  Otherwise, we'd have to
-        # special case the first and last histogram in the group.
-        if use_counter_group:
-            print("  HistogramFirstUseCounter,", file=output)
-            print("  HistogramDUMMY1 = HistogramFirstUseCounter - 1,", file=output)
+    seen_group_types = {"UseCounter": False, "UseCounterWorker": False}
+    for (group_type, histograms) in groups:
+        if group_type is not None:
+            assert isinstance(group_type, basestring)
+            assert group_type in seen_group_types.keys()
+            assert not seen_group_types[group_type]
+            seen_group_types[group_type] = True
+            # The Histogram*DUMMY enum variables are used to make the computation
+            # of Histogram{First,Last}* easier.  Otherwise, we'd have to special
+            # case the first and last histogram in the group.
+            print("  HistogramFirst%s," % group_type, file=output)
+            print("  Histogram{0}DUMMY1 = HistogramFirst{0} - 1,".format(group_type), file=output)
         for histogram in histograms:
             if histogram.record_on_os(buildconfig.substs["OS_TARGET"]):
                 print("  %s," % histogram.name(), file=output)
-        if use_counter_group:
-            print("  HistogramDUMMY2,", file=output)
-            print("  HistogramLastUseCounter = HistogramDUMMY2 - 1,", file=output)
+        if group_type is not None:
+            assert isinstance(group_type, basestring)
+            print("  Histogram%sDUMMY2," % group_type, file=output)
+            print("  HistogramLast{0} = Histogram{0}DUMMY2 - 1,".format(group_type), file=output)
     print("  HistogramCount,", file=output)
-    if seen_use_counters:
-        print("  HistogramUseCounterCount = HistogramLastUseCounter -"
-              " HistogramFirstUseCounter + 1", file=output)
-    else:
-        print("  HistogramFirstUseCounter = 0,", file=output)
-        print("  HistogramLastUseCounter = 0,", file=output)
-        print("  HistogramUseCounterCount = 0", file=output)
+    for (key, value) in seen_group_types.items():
+        if value:
+            print("  Histogram{0}Count = HistogramLast{0} - HistogramFirst{0} + 1,"
+                  .format(key), file=output)
+        else:
+            print("  HistogramFirst%s = 0," % key, file=output)
+            print("  HistogramLast%s = 0," % key, file=output)
+            print("  Histogram%sCount = 0," % key, file=output)
     print("};", file=output)
     # Write categorical label enums.
     categorical = filter(lambda h: h.kind() == "categorical", all_histograms)
     categorical = filter(lambda h: h.record_on_os(buildconfig.substs["OS_TARGET"]), categorical)
     enums = [("LABELS_" + h.name(), h.labels(), h.name()) for h in categorical]
     for name, labels, _ in enums:
         print("\nenum class %s : uint32_t {" % name, file=output)
--- a/toolkit/components/telemetry/build_scripts/mozparsers/parse_histograms.py
+++ b/toolkit/components/telemetry/build_scripts/mozparsers/parse_histograms.py
@@ -720,16 +720,20 @@ def from_json(filename, strict_type_chec
             ParserError("error parsing histograms in %s: %s" % (filename, e.message)).handle_now()
     return histograms
 def from_UseCounters_conf(filename, strict_type_checks):
     return usecounters.generate_histograms(filename)
+def from_UseCountersWorker_conf(filename, strict_type_checks):
+    return usecounters.generate_histograms(filename, True)
 def from_nsDeprecatedOperationList(filename, strict_type_checks):
     operation_regex = re.compile('^DEPRECATED_OPERATION\\(([^)]+)\\)')
     histograms = collections.OrderedDict()
     with open(filename, 'r') as f:
         for line in f:
             match = operation_regex.search(line)
             if not match:
@@ -823,16 +827,18 @@ FILENAME_PARSERS = [
 # Similarly to the dance above with buildconfig, usecounters may not be
 # available, so handle that gracefully.
     import usecounters
     FILENAME_PARSERS.append(lambda x: from_UseCounters_conf if x == 'UseCounters.conf' else None)
+        lambda x: from_UseCountersWorker_conf if x == 'UseCountersWorker.conf' else None)
 except ImportError:
 def from_files(filenames, strict_type_checks=True):
     """Return an iterator that provides a sequence of Histograms for
 the histograms defined in filenames.
@@ -859,27 +865,36 @@ the histograms defined in filenames.
         if not isinstance(histograms, OrderedDict):
             ParserError("Histogram parser did not provide an OrderedDict.").handle_now()
         for (name, definition) in histograms.iteritems():
             if name in all_histograms:
                 ParserError('Duplicate histogram name "%s".' % name).handle_later()
             all_histograms[name] = definition
-    # We require that all USE_COUNTER2_* histograms be defined in a contiguous
+    def check_continuity(iterable, filter_function, name):
+        indices = filter(filter_function, enumerate(iterable.iterkeys()))
+        if indices:
+            lower_bound = indices[0][0]
+            upper_bound = indices[-1][0]
+            n_counters = upper_bound - lower_bound + 1
+            if n_counters != len(indices):
+                ParserError("Histograms %s must be defined in a contiguous block." %
+                            name).handle_later()
+    # We require that all USE_COUNTER2_*_WORKER histograms be defined in a contiguous
     # block.
-    use_counter_indices = filter(lambda x: x[1].startswith("USE_COUNTER2_"),
-                                 enumerate(all_histograms.iterkeys()))
-    if use_counter_indices:
-        lower_bound = use_counter_indices[0][0]
-        upper_bound = use_counter_indices[-1][0]
-        n_counters = upper_bound - lower_bound + 1
-        if n_counters != len(use_counter_indices):
-            ParserError("Use counter histograms must be defined in a contiguous block."
-                        ).handle_later()
+    check_continuity(all_histograms,
+                     lambda x: x[1].startswith("USE_COUNTER2_") and x[1].endswith("_WORKER"),
+                     "use counter worker")
+    # And all other USE_COUNTER2_* histograms be defined in a contiguous
+    # block.
+    check_continuity(all_histograms,
+                     lambda x: x[1].startswith("USE_COUNTER2_") and not x[1].endswith("_WORKER"),
+                     "use counter")
     # Check that histograms that were removed from Histograms.json etc.
     # are also removed from the allowlists.
     if allowlists is not None:
         all_allowlist_entries = itertools.chain.from_iterable(allowlists.itervalues())
         orphaned = set(all_allowlist_entries) - set(all_histograms.keys())
         if len(orphaned) > 0:
             msg = 'The following entries are orphaned and should be removed from ' \
--- a/toolkit/components/telemetry/histogram-allowlists.json
+++ b/toolkit/components/telemetry/histogram-allowlists.json
@@ -1183,16 +1183,17 @@
@@ -1254,17 +1255,19 @@
--- a/toolkit/components/telemetry/moz.build
+++ b/toolkit/components/telemetry/moz.build
@@ -138,16 +138,17 @@ PYTHON_UNITTEST_MANIFESTS += [
 # Generate histogram files.
 histogram_files = [
+    '/dom/base/UseCountersWorker.conf',