summaryrefslogtreecommitdiffstats
path: root/dom/base/usecounters.py
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/usecounters.py')
-rw-r--r--dom/base/usecounters.py792
1 files changed, 792 insertions, 0 deletions
diff --git a/dom/base/usecounters.py b/dom/base/usecounters.py
new file mode 100644
index 0000000000..0071be7dff
--- /dev/null
+++ b/dom/base/usecounters.py
@@ -0,0 +1,792 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import re
+
+
+def read_conf(conf_filename):
+ # Can't read/write from a single StringIO, so make a new one for reading.
+ stream = open(conf_filename, "r")
+
+ def parse_counters(stream):
+ for line_num, full_line in enumerate(stream):
+ line = full_line.rstrip("\n")
+ if not line or line.startswith("//"):
+ # empty line or comment
+ continue
+ m = re.match(r"method ([A-Za-z0-9]+)\.([A-Za-z0-9]+)$", line)
+ if m:
+ interface_name, method_name = m.groups()
+ yield {
+ "type": "method",
+ "interface_name": interface_name,
+ "method_name": method_name,
+ }
+ continue
+ m = re.match(r"attribute ([A-Za-z0-9]+)\.([A-Za-z0-9]+)$", line)
+ if m:
+ interface_name, attribute_name = m.groups()
+ yield {
+ "type": "attribute",
+ "interface_name": interface_name,
+ "attribute_name": attribute_name,
+ }
+ continue
+ m = re.match(r"custom ([A-Za-z0-9_]+) (.*)$", line)
+ if m:
+ name, desc = m.groups()
+ yield {"type": "custom", "name": name, "desc": desc}
+ continue
+ raise ValueError(
+ "error parsing %s at line %d" % (conf_filename, line_num + 1)
+ )
+
+ return parse_counters(stream)
+
+
+YAML_HEADER = """\
+# This file is AUTOGENERATED by usecounters.py. DO NOT EDIT.
+# (instead, re-run ./mach gen-use-counter-metrics)
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+---
+$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
+$tags:
+ - 'Core :: DOM: Core & HTML'
+
+"""
+
+BASE_METRICS = """\
+use.counter:
+ content_documents_destroyed:
+ type: counter
+ description: >
+ A count of how many content documents were destroyed.
+ Used to turn document use counters' counts into rates.
+ Excludes documents for which we do not count use counters
+ (See `Document::ShouldIncludeInTelemetry`).
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1204994
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1845779
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
+ notification_emails:
+ - dom-core@mozilla.com
+ - emilio@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
+ top_level_content_documents_destroyed:
+ type: counter
+ description: >
+ A count of how many "pages" were destroyed.
+ Used to turn page use counters' counts into rates.
+ Excludes pages that contain only documents for which we do not count use
+ counters (See `Document::ShouldIncludeInTelemetry`).
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1204994
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1845779
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
+ notification_emails:
+ - dom-core@mozilla.com
+ - emilio@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
+ dedicated_workers_destroyed:
+ type: counter
+ description: >
+ A count of how many `Dedicated`-kind workers were destroyed.
+ Used to turn dedicated worker use counters' counts into rates.
+ Excludes chrome workers.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
+ notification_emails:
+ - dom-core@mozilla.com
+ - emilio@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
+ shared_workers_destroyed:
+ type: counter
+ description: >
+ A count of how many `Shared`-kind workers were destroyed.
+ Used to turn shared worker use counters' counts into rates.
+ Excludes chrome workers.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
+ notification_emails:
+ - dom-core@mozilla.com
+ - emilio@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
+ service_workers_destroyed:
+ type: counter
+ description: >
+ A count of how many `Service`-kind workers were destroyed.
+ Used to turn service worker use counters' counts into rates.
+ Excludes chrome workers.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
+ notification_emails:
+ - dom-core@mozilla.com
+ - emilio@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
+"""
+
+USE_COUNTER_TEMPLATE = """\
+ {name}:
+ type: counter
+ description: >
+ {desc}
+ Compare against `{denominator}`
+ to calculate the rate.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ notification_emails:
+ - dom-core@mozilla.com
+ - emilio@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
+"""
+
+
+def gen_use_counter_metrics():
+ """
+ Finds use counters in:
+ * dom/base/UseCounters.conf
+ * dom/base/UseCountersWorker.conf
+ * dom/base/nsDeprecatedOperationsList.h
+ * !/layout/style/ServoCSSPropList.py
+ * servo/components/style/properties/counted_unknown_properties.py
+ and overwrites the Glean metrics definition file
+ `dom/base/use_counter_metrics.yaml` with definitions for each use counter found.
+
+ IF YOU CHANGE THIS FUNCTION:
+ * You should probably add your bug's number to USE_COUNTER_TEMPLATE, above.
+
+ Returns 0 on success.
+ """
+ (
+ page,
+ doc,
+ dedicated,
+ shared,
+ service,
+ ops_page,
+ ops_doc,
+ css_page,
+ css_doc,
+ ) = parse_use_counters()
+ import os
+
+ import buildconfig
+ from mozbuild.util import FileAvoidWrite
+
+ yaml_path = os.path.join(
+ buildconfig.topsrcdir, "dom", "base", "use_counter_metrics.yaml"
+ )
+ with FileAvoidWrite(yaml_path) as f:
+ f.write(YAML_HEADER)
+ f.write(BASE_METRICS)
+
+ total = (
+ len(page)
+ + len(doc)
+ + len(dedicated)
+ + len(shared)
+ + len(service)
+ + len(ops_page)
+ + len(ops_doc)
+ + len(css_page)
+ + len(css_doc)
+ )
+ f.write(f"# Total of {total} use counter metrics (excludes denominators).\n")
+ f.write(f"# Total of {len(page)} 'page' use counters.\n")
+ f.write("use.counter.page:\n")
+ for [_, name, desc] in page:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.top_level_content_documents_destroyed",
+ )
+ )
+
+ f.write(f"# Total of {len(doc)} 'document' use counters.\n")
+ f.write("use.counter.doc:\n")
+ for [_, name, desc] in doc:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.content_documents_destroyed",
+ )
+ )
+
+ f.write(f"# Total of {len(dedicated)} 'dedicated worker' use counters.\n")
+ f.write("use.counter.worker.dedicated:\n")
+ for [_, name, desc] in dedicated:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.dedicated_workers_destroyed",
+ )
+ )
+
+ f.write(f"# Total of {len(shared)} 'shared worker' use counters.\n")
+ f.write("use.counter.worker.shared:\n")
+ for [_, name, desc] in shared:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.shared_workers_destroyed",
+ )
+ )
+
+ f.write(f"# Total of {len(service)} 'service worker' use counters.\n")
+ f.write("use.counter.worker.service:\n")
+ for [_, name, desc] in service:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.service_workers_destroyed",
+ )
+ )
+
+ f.write(
+ f"# Total of {len(ops_page)} 'deprecated operations (page)' use counters.\n"
+ )
+ f.write("use.counter.deprecated_ops.page:\n")
+ for [_, name, desc] in ops_page:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.top_level_content_documents_destroyed",
+ )
+ )
+
+ f.write(
+ f"# Total of {len(ops_doc)} 'deprecated operations (document)' use counters.\n"
+ )
+ f.write("use.counter.deprecated_ops.doc:\n")
+ for [_, name, desc] in ops_doc:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.content_documents_destroyed",
+ )
+ )
+
+ f.write(f"# Total of {len(css_page)} 'CSS (page)' use counters.\n")
+ f.write("use.counter.css.page:\n")
+ for [_, name, desc] in css_page:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.top_level_content_documents_destroyed",
+ )
+ )
+
+ f.write(f"# Total of {len(css_doc)} 'CSS (document)' use counters.\n")
+ f.write("use.counter.css.doc:\n")
+ for [_, name, desc] in css_doc:
+ f.write(
+ USE_COUNTER_TEMPLATE.format(
+ name=name,
+ desc=desc,
+ denominator="use.counter.content_documents_destroyed",
+ )
+ )
+
+ return 0
+
+
+def parse_use_counters():
+ """
+ Finds use counters in:
+ * dom/base/UseCounters.conf
+ * dom/base/UseCountersWorker.conf
+ * dom/base/nsDeprecatedOperationsList.h
+ * !/layout/style/ServoCSSPropList.py
+ * servo/components/style/properties/counted_unknown_properties.py
+ and returns them as a tuple of lists of tuples of the form:
+ (page, doc, dedicated, shared, service, ops_page, ops_doc, css_page, css_doc)
+ where each of the items is a List<Tuple<enum_name, glean_name, description>>
+ where `enum_name` is the name of the enum variant from UseCounter.h
+ (like `eUseCounter_custom_CustomizedBuiltin`), and
+ where `glean_name` is the name conjugated to Glean metric name safety.
+ """
+
+ # Note, this function contains a duplication of enum naming logic from UseCounter.h.
+ # If you change the enum name format, you'll need to do it twice.
+
+ # There are 3 kinds of Use Counters in conf files: method, attribute, custom.
+ # `method` and `attribute` are presumed label-safe and are taken as-is.
+ # `custom` can be any case, so are coerced to snake_case.
+ import os
+
+ import buildconfig
+
+ uc_path = os.path.join(buildconfig.topsrcdir, "dom", "base", "UseCounters.conf")
+ page = []
+ doc = []
+ for counter in read_conf(uc_path):
+ if counter["type"] == "method":
+ enum_name = (
+ f"eUseCounter_{counter['interface_name']}_{counter['method_name']}"
+ )
+ glean_name = f"{counter['interface_name']}_{counter['method_name']}".lower()
+ method = f"called {counter['interface_name']}.{counter['method_name']}"
+ page.append((enum_name, glean_name, f"Whether a page called {method}."))
+ doc.append((enum_name, glean_name, f"Whether a document called {method}."))
+ elif counter["type"] == "attribute":
+ enum_root = (
+ f"eUseCounter_{counter['interface_name']}_{counter['attribute_name']}"
+ )
+ name = f"{counter['interface_name']}_{counter['attribute_name']}".lower()
+ attr = f"{counter['interface_name']}.{counter['attribute_name']}"
+ page.append(
+ (f"{enum_root}_getter", f"{name}_getter", f"Whether a page got {attr}.")
+ )
+ page.append(
+ (f"{enum_root}_setter", f"{name}_setter", f"Whether a page set {attr}.")
+ )
+ doc.append(
+ (
+ f"{enum_root}_getter",
+ f"{name}_getter",
+ f"Whether a document got {attr}.",
+ )
+ )
+ doc.append(
+ (
+ f"{enum_root}_setter",
+ f"{name}_setter",
+ f"Whether a document set {attr}.",
+ )
+ )
+ elif counter["type"] == "custom":
+ enum_name = f"eUseCounter_custom_{counter['name']}"
+ page.append(
+ (
+ enum_name,
+ to_snake_case(counter["name"]),
+ f"Whether a page {counter['desc']}.",
+ )
+ )
+ doc.append(
+ (
+ enum_name,
+ to_snake_case(counter["name"]),
+ f"Whether a document {counter['desc']}.",
+ )
+ )
+ else:
+ print(f"Found unexpected use counter type {counter['type']}. Returning 1.")
+ return 1
+
+ worker_uc_path = os.path.join(
+ buildconfig.topsrcdir, "dom", "base", "UseCountersWorker.conf"
+ )
+ dedicated = []
+ shared = []
+ service = []
+ for counter in read_conf(worker_uc_path):
+ if counter["type"] == "method":
+ enum_name = f"{counter['interface_name']}_{counter['method_name']}"
+ name = f"{counter['interface_name']}_{counter['method_name']}".lower()
+ method = f"called {counter['interface_name']}.{counter['method_name']}"
+ dedicated.append(
+ (enum_name, name, f"Whether a dedicated worker called {method}.")
+ )
+ shared.append(
+ (enum_name, name, f"Whether a shared worker called {method}.")
+ )
+ service.append(
+ (enum_name, name, f"Whether a service worker called {method}.")
+ )
+ elif counter["type"] == "attribute":
+ enum_root = f"{counter['interface_name']}_{counter['attribute_name']}"
+ name = f"{counter['interface_name']}_{counter['attribute_name']}".lower()
+ attr = f"{counter['interface_name']}.{counter['attribute_name']}"
+ dedicated.append(
+ (
+ f"{enum_root}_getter",
+ f"{name}_getter",
+ f"Whether a dedicated worker got {attr}.",
+ )
+ )
+ dedicated.append(
+ (
+ f"{enum_root}_setter",
+ f"{name}_setter",
+ f"Whether a dedicated worker set {attr}.",
+ )
+ )
+ shared.append(
+ (
+ f"{enum_root}_getter",
+ f"{name}_getter",
+ f"Whether a shared worker got {attr}.",
+ )
+ )
+ shared.append(
+ (
+ f"{enum_root}_setter",
+ f"{name}_setter",
+ f"Whether a shared worker set {attr}.",
+ )
+ )
+ service.append(
+ (
+ f"{enum_root}_getter",
+ f"{name}_getter",
+ f"Whether a service worker got {attr}.",
+ )
+ )
+ service.append(
+ (
+ f"{enum_root}_setter",
+ f"{name}_setter",
+ f"Whether a service worker set {attr}.",
+ )
+ )
+ elif counter["type"] == "custom":
+ enum_name = f"Custom_{counter['name']}"
+ dedicated.append(
+ (
+ enum_name,
+ to_snake_case(counter["name"]),
+ f"Whether a dedicated worker {counter['desc']}.",
+ )
+ )
+ shared.append(
+ (
+ enum_name,
+ to_snake_case(counter["name"]),
+ f"Whether a shared worker {counter['desc']}.",
+ )
+ )
+ service.append(
+ (
+ enum_name,
+ to_snake_case(counter["name"]),
+ f"Whether a service worker {counter['desc']}.",
+ )
+ )
+ else:
+ print(
+ f"Found unexpected worker use counter type {counter['type']}. Returning 1."
+ )
+ return 1
+
+ # nsDeprecatedOperationsList.h parsing is adapted from parse_histograms.py.
+ operation_list_path = os.path.join(
+ buildconfig.topsrcdir, "dom", "base", "nsDeprecatedOperationList.h"
+ )
+ operation_regex = re.compile("^DEPRECATED_OPERATION\\(([^)]+)\\)")
+ ops_page = []
+ ops_doc = []
+ with open(operation_list_path) as f:
+ for line in f:
+ match = operation_regex.search(line)
+ if not match:
+ # No macro, probably whitespace or comment.
+ continue
+
+ op = match.group(1)
+ op_name = to_snake_case(op)
+ enum_name = f"eUseCounter_{op}"
+ ops_page.append((enum_name, op_name, f"Whether a page used {op}."))
+ ops_doc.append((enum_name, op_name, f"Whether a document used {op}."))
+
+ # Theoretically, we could do this without a completed build
+ # (ie, without the generated ServoCSSPropList.py) by sourcing direct from
+ # servo/components/style/properties/data.py:PropertiesData(engine=gecko).
+ #
+ # ...but parse_histograms.py doesn't do this the hard way. Should we?
+
+ import runpy
+
+ proplist_path = os.path.join(
+ buildconfig.topobjdir, "layout", "style", "ServoCSSPropList.py"
+ )
+ css_properties = runpy.run_path(proplist_path)["data"]
+ css_page = []
+ css_doc = []
+ for prop in css_properties.values():
+ # We prefix `prop_name` with `css_` to avoid colliding with C++ keywords
+ # like `float`.
+ prop_name = "css_" + to_snake_case(prop.name)
+
+ # Dependency keywords: CSS_PROP_PUBLIC_OR_PRIVATE, GenerateServoCSSPropList.py.
+ method = "Float" if prop.method == "CssFloat" else prop.method
+ # Dependency keywords: CSS_PROP_DOMPROP_PREFIXED, GenerateServoCSSPropList.py.
+ if method.startswith("Moz") and prop.type() != "alias":
+ method = method[3:] # remove the moz prefix
+
+ enum_name = f"eUseCounter_property_{method}"
+ css_page.append(
+ (enum_name, prop_name, f"Whether a page used the CSS property {prop.name}.")
+ )
+ css_doc.append(
+ (
+ enum_name,
+ prop_name,
+ f"Whether a document used the CSS property {prop.name}.",
+ )
+ )
+
+ # Counted unknown properties: AKA - stuff that doesn't exist, but we want
+ # to count uses of anyway.
+ # We _might_ decide to implement these in the future, though, so we just add
+ # them to the css_page, css_doc lists directly for continuity.
+ # (We do give them a different description, though)
+
+ import sys
+
+ sys.path.append(os.path.join(buildconfig.topsrcdir, "layout", "style"))
+ from GenerateCountedUnknownProperties import to_camel_case
+
+ unknown_proplist_path = os.path.join(
+ buildconfig.topsrcdir,
+ "servo",
+ "components",
+ "style",
+ "properties",
+ "counted_unknown_properties.py",
+ )
+ unknown_properties = runpy.run_path(unknown_proplist_path)[
+ "COUNTED_UNKNOWN_PROPERTIES"
+ ]
+ for prop in unknown_properties:
+ enum_name = f"eUseCounter_unknown_property_{to_camel_case(prop)}"
+ prop_name = to_snake_case(prop)
+ css_page.append(
+ (
+ enum_name,
+ prop_name,
+ f"Whether a page used the (unknown, counted) CSS property {prop}.",
+ )
+ )
+ css_doc.append(
+ (
+ enum_name,
+ prop_name,
+ f"Whether a document used the (unknown, counted) CSS property {prop}.",
+ )
+ )
+
+ return (page, doc, dedicated, shared, service, ops_page, ops_doc, css_page, css_doc)
+
+
+def to_snake_case(kebab_or_pascal):
+ """
+ Takes `kebab_or_pascal` which is in PascalCase or kebab-case
+ and conjugates it to "snake_case" (all lowercase, "_"-delimited).
+ """
+ return (
+ re.sub("([A-Z]+)", r"_\1", kebab_or_pascal).replace("-", "_").lower().strip("_")
+ )
+
+
+def metric_map(f, *inputs):
+ """
+ Parses all use counters and outputs UseCounter.cpp which contains implementations
+ for two functions defined in UseCounter.h:
+ * const char* IncrementUseCounter(UseCounter aUseCounter, bool aIsPage)
+ * const char* IncrementWorkerUseCounter(UseCounterWorker aUseCounter, dom::WorkerKind aKind)
+
+ (Basically big switch statements mapping from enums to glean metrics, calling Add())
+ """
+
+ (
+ page,
+ doc,
+ dedicated,
+ shared,
+ service,
+ ops_page,
+ ops_doc,
+ css_page,
+ css_doc,
+ ) = parse_use_counters()
+
+ f.write(
+ """\
+/* AUTOGENERATED by usecounters.py. DO NOT EDIT */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/UseCounterMetrics.h"
+
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/glean/GleanMetrics.h"
+
+namespace mozilla::dom {
+
+const char* IncrementUseCounter(UseCounter aUseCounter, bool aIsPage) {
+ static constexpr struct {
+ const char* name;
+ glean::impl::CounterMetric doc_metric;
+ glean::impl::CounterMetric page_metric;
+ } kEntries[] = {
+"""
+ )
+
+ # This order must match the order UseCounter is defined,
+ # (and we guarantee it via the MOZ_ASSERT below at runtime).
+ assert len(page) == len(doc)
+ assert len(ops_page) == len(ops_doc)
+ assert len(css_page) == len(css_doc)
+
+ index = 0
+ static_asserts = []
+ for pc, dc in zip(page, doc):
+ assert pc[0] == dc[0]
+ assert pc[1] == dc[1]
+ static_asserts.append(f"static_assert({index} == size_t(UseCounter::{pc[0]}));")
+ f.write(
+ f"""\
+ {{
+ "{pc[1]}",
+ glean::use_counter_doc::{pc[1]},
+ glean::use_counter_page::{pc[1]},
+ }},
+"""
+ )
+ index += 1
+
+ for pc, dc in zip(ops_page, ops_doc):
+ assert pc[0] == dc[0]
+ assert pc[1] == dc[1]
+ static_asserts.append(f"static_assert({index} == size_t(UseCounter::{pc[0]}));")
+ f.write(
+ f"""\
+ {{
+ "deprecated_ops.{pc[1]}",
+ glean::use_counter_deprecated_ops_doc::{pc[1]},
+ glean::use_counter_deprecated_ops_page::{pc[1]},
+ }},
+"""
+ )
+ index += 1
+
+ for pc, dc in zip(css_page, css_doc):
+ assert pc[0] == dc[0]
+ assert pc[1] == dc[1]
+ static_asserts.append(f"static_assert({index} == size_t(UseCounter::{pc[0]}));")
+ f.write(
+ f"""\
+ {{
+ "css.{pc[1]}",
+ glean::use_counter_css_doc::{pc[1]},
+ glean::use_counter_css_page::{pc[1]},
+ }},
+"""
+ )
+ index += 1
+
+ f.write("};\n")
+ f.write("\n".join(static_asserts))
+ f.write(
+ """\
+ MOZ_ASSERT(size_t(aUseCounter) < ArrayLength(kEntries));
+ const auto& entry = kEntries[size_t(aUseCounter)];
+ (aIsPage ? entry.page_metric : entry.doc_metric).Add();
+ return entry.name;
+}
+
+const char* IncrementWorkerUseCounter(UseCounterWorker aUseCounter, WorkerKind aKind) {
+ static constexpr struct {
+ const char* name;
+ glean::impl::CounterMetric dedicated_metric;
+ glean::impl::CounterMetric shared_metric;
+ glean::impl::CounterMetric service_metric;
+ } kEntries[] = {
+"""
+ )
+ assert len(dedicated) == len(shared)
+ assert len(dedicated) == len(service)
+ index = 0
+ static_asserts = []
+ for dc, sc, servicec in zip(dedicated, shared, service):
+ assert dc[0] == sc[0]
+ assert dc[1] == sc[1]
+ assert dc[0] == servicec[0]
+ assert dc[1] == servicec[1]
+ static_asserts.append(
+ f"static_assert({index} == size_t(UseCounterWorker::{dc[0]}));"
+ )
+ f.write(
+ f"""\
+ {{
+ "{dc[1]}",
+ glean::use_counter_worker_dedicated::{dc[1]},
+ glean::use_counter_worker_shared::{dc[1]},
+ glean::use_counter_worker_service::{dc[1]},
+ }},
+"""
+ )
+ index += 1
+ f.write("};\n")
+ f.write("\n".join(static_asserts))
+ f.write(
+ """\
+ MOZ_ASSERT(size_t(aUseCounter) < ArrayLength(kEntries));
+ const auto& entry = kEntries[size_t(aUseCounter)];
+ switch (aKind) {
+ case WorkerKind::WorkerKindDedicated:
+ entry.dedicated_metric.Add();
+ break;
+ case WorkerKind::WorkerKindShared:
+ entry.shared_metric.Add();
+ break;
+ case WorkerKind::WorkerKindService:
+ entry.service_metric.Add();
+ break;
+ }
+ return entry.name;
+}
+
+} // namespace mozilla
+"""
+ )