Bug 1852098 - mach command gen-use-counter-metrics r=emilio,firefox-build-system-reviewers,ahochheiden

Takes use counter definitions and generates a use_counter_metrics.yaml with
`counter` metric definitions for each use counter and denominator.

Total use counters at time of writing: 2271 (excludes denominators)

Differential Revision: https://phabricator.services.mozilla.com/D193246
This commit is contained in:
Chris H-C 2023-11-21 17:09:52 +00:00
parent 4129adf3e2
commit efd2596fde
6 changed files with 36944 additions and 0 deletions

21
dom/base/mach_commands.py Normal file
View file

@ -0,0 +1,21 @@
# 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/.
from mach.decorators import Command
@Command(
"gen-use-counter-metrics",
category="misc",
description="Generate a Glean use_counter_metrics.yaml file, creating metrics definitions for every use counter.",
)
def gen_use_counter_metrics(command_context):
# Dispatch to usecounters.py
import sys
from os import path
sys.path.append(path.dirname(__file__))
from usecounters import gen_use_counter_metrics
return gen_use_counter_metrics()

File diff suppressed because it is too large Load diff

View file

@ -87,3 +87,448 @@ def generate_histograms(filename, is_for_worker=False):
append_counters(counter["name"].upper(), counter["desc"])
return items
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'
"""
# TODO: What are good notification emails?
# TODO: What's good info to include in these descriptions?
DENOMINATOR_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
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
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
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
notification_emails:
- dom-core@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
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
notification_emails:
- dom-core@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
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
notification_emails:
- dom-core@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
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.
"""
# 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":
name = f"{counter['interface_name']}_{counter['method_name']}".lower()
method = f"called {counter['interface_name']}.{counter['method_name']}"
page.append((name, f"Whether a page called {method}."))
doc.append((name, f"Whether a document called {method}."))
elif counter["type"] == "attribute":
name = f"{counter['interface_name']}_{counter['attribute_name']}".lower()
attr = f"{counter['interface_name']}.{counter['attribute_name']}"
page.append((f"{name}_getter", f"Whether a page got {attr}."))
page.append((f"{name}_setter", f"Whether a page set {attr}."))
doc.append((f"{name}_getter", f"Whether a document got {attr}."))
doc.append((f"{name}_setter", f"Whether a document set {attr}."))
elif counter["type"] == "custom":
page.append(
(to_snake_case(counter["name"]), f"Whether a page {counter['desc']}.")
)
doc.append(
(
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":
name = f"{counter['interface_name']}_{counter['method_name']}".lower()
method = f"called {counter['interface_name']}.{counter['method_name']}"
dedicated.append((name, f"Whether a dedicated worker called {method}."))
shared.append((name, f"Whether a shared worker called {method}."))
service.append((name, f"Whether a service worker called {method}."))
elif counter["type"] == "attribute":
name = f"{counter['interface_name']}_{counter['attribute_name']}".lower()
attr = f"{counter['interface_name']}.{counter['attribute_name']}"
dedicated.append((name, f"Whether a dedicated worker got {attr}."))
dedicated.append((name, f"Whether a dedicated worker set {attr}."))
shared.append((name, f"Whether a shared worker got {attr}."))
shared.append((name, f"Whether a shared worker set {attr}."))
service.append((name, f"Whether a service worker got {attr}."))
service.append((name, f"Whether a service worker set {attr}."))
elif counter["type"] == "custom":
dedicated.append(
(
to_snake_case(counter["name"]),
f"Whether a dedicated worker {counter['desc']}.",
)
)
shared.append(
(
to_snake_case(counter["name"]),
f"Whether a shared worker {counter['desc']}.",
)
)
service.append(
(
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)
ops_page.append((op_name, f"Whether a page used {op}."))
ops_doc.append((op_name, f"Whether a document used {op}."))
# TODO: 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)
css_page.append(
(prop_name, f"Whether a page used the CSS property {prop.name}.")
)
css_doc.append(
(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)
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:
prop_name = to_snake_case(prop)
css_page.append(
(
prop_name,
f"Whether a page used the (unknown, counted) CSS property {prop}.",
)
)
css_doc.append(
(
prop_name,
f"Whether a document used the (unknown, counted) CSS property {prop}.",
)
)
from mozbuild.util import FileAvoidWrite
# TODO: Up for discussion: organization.
# Especially e.g. use.counter.css.page.css_float, but also: perhaps suffixes are preferred over categories?
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(DENOMINATOR_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 uc in page:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in doc:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in dedicated:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in shared:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in service:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in ops_page:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in ops_doc:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in css_page:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
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 uc in css_doc:
f.write(
USE_COUNTER_TEMPLATE.format(
name=uc[0],
desc=uc[1],
denominator="use.counter.content_documents_destroyed",
)
)
return 0
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("_")
)

View file

@ -23,3 +23,23 @@ pageload:
notification_emails:
- perf-telemetry-alerts@mozilla.com
- dpalmeiro@mozilla.com
use-counters:
description: |
Collects counts of uses of web platform features.
See [Use Counters Documentation](https://firefox-source-docs.mozilla.org/dom/use-counters.html)
for more information.
reasons:
app_shutdown_confirmed: |
Submitted when we reach `ShutdownPhase::AppShutdownConfirmed`.
(Any later and the network might have been torn down.)
include_client_id: true
send_if_empty: false
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
- chutten@mozilla.com
- emilio@mozilla.com

View file

@ -103,6 +103,7 @@ MACH_COMMANDS = {
"geckoview-junit": MachCommandReference(
"testing/mochitest/mach_commands.py", ["test"]
),
"gen-use-counter-metrics": MachCommandReference("dom/base/mach_commands.py"),
"generate-test-certs": MachCommandReference(
"security/manager/tools/mach_commands.py"
),

View file

@ -16,6 +16,7 @@
# Order is lexicographical, enforced by t/c/glean/tests/pytest/test_yaml_indices.py
gecko_metrics = [
"browser/base/content/metrics.yaml",
"dom/base/use_counter_metrics.yaml",
"dom/media/metrics.yaml",
"dom/media/webrtc/metrics.yaml",
"dom/metrics.yaml",