mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			792 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			792 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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
 | 
						|
"""
 | 
						|
    )
 |