1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 def read_conf(conf_filename
):
9 # Can't read/write from a single StringIO, so make a new one for reading.
10 stream
= open(conf_filename
, "r")
12 def parse_counters(stream
):
13 for line_num
, full_line
in enumerate(stream
):
14 line
= full_line
.rstrip("\n")
15 if not line
or line
.startswith("//"):
16 # empty line or comment
18 m
= re
.match(r
"method ([A-Za-z0-9]+)\.([A-Za-z0-9]+)$", line
)
20 interface_name
, method_name
= m
.groups()
23 "interface_name": interface_name
,
24 "method_name": method_name
,
27 m
= re
.match(r
"attribute ([A-Za-z0-9]+)\.([A-Za-z0-9]+)$", line
)
29 interface_name
, attribute_name
= m
.groups()
32 "interface_name": interface_name
,
33 "attribute_name": attribute_name
,
36 m
= re
.match(r
"custom ([A-Za-z0-9_]+) (.*)$", line
)
38 name
, desc
= m
.groups()
39 yield {"type": "custom", "name": name
, "desc": desc
}
42 "error parsing %s at line %d" % (conf_filename
, line_num
+ 1)
45 return parse_counters(stream
)
49 # This file is AUTOGENERATED by usecounters.py. DO NOT EDIT.
50 # (instead, re-run ./mach gen-use-counter-metrics)
52 # This Source Code Form is subject to the terms of the Mozilla Public
53 # License, v. 2.0. If a copy of the MPL was not distributed with this
54 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
57 $schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
59 - 'Core :: DOM: Core & HTML'
65 content_documents_destroyed:
68 A count of how many content documents were destroyed.
69 Used to turn document use counters' counts into rates.
70 Excludes documents for which we do not count use counters
71 (See `Document::ShouldIncludeInTelemetry`).
73 - https://bugzilla.mozilla.org/show_bug.cgi?id=1204994
74 - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
75 - https://bugzilla.mozilla.org/show_bug.cgi?id=1845779
76 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
78 - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
80 - dom-core@mozilla.com
86 top_level_content_documents_destroyed:
89 A count of how many "pages" were destroyed.
90 Used to turn page use counters' counts into rates.
91 Excludes pages that contain only documents for which we do not count use
92 counters (See `Document::ShouldIncludeInTelemetry`).
94 - https://bugzilla.mozilla.org/show_bug.cgi?id=1204994
95 - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
96 - https://bugzilla.mozilla.org/show_bug.cgi?id=1845779
97 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
99 - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672
101 - dom-core@mozilla.com
107 dedicated_workers_destroyed:
110 A count of how many `Dedicated`-kind workers were destroyed.
111 Used to turn dedicated worker use counters' counts into rates.
112 Excludes chrome workers.
114 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
115 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
117 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
119 - dom-core@mozilla.com
125 shared_workers_destroyed:
128 A count of how many `Shared`-kind workers were destroyed.
129 Used to turn shared worker use counters' counts into rates.
130 Excludes chrome workers.
132 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
133 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
135 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
137 - dom-core@mozilla.com
143 service_workers_destroyed:
146 A count of how many `Service`-kind workers were destroyed.
147 Used to turn service worker use counters' counts into rates.
148 Excludes chrome workers.
150 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
151 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
153 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706
155 - dom-core@mozilla.com
163 USE_COUNTER_TEMPLATE
= """\
168 Compare against `{denominator}`
169 to calculate the rate.
171 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
173 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
175 - dom-core@mozilla.com
184 def gen_use_counter_metrics():
186 Finds use counters in:
187 * dom/base/UseCounters.conf
188 * dom/base/UseCountersWorker.conf
189 * dom/base/nsDeprecatedOperationsList.h
190 * !/layout/style/ServoCSSPropList.py
191 * servo/components/style/properties/counted_unknown_properties.py
192 and overwrites the Glean metrics definition file
193 `dom/base/use_counter_metrics.yaml` with definitions for each use counter found.
195 IF YOU CHANGE THIS FUNCTION:
196 * You should probably add your bug's number to USE_COUNTER_TEMPLATE, above.
198 Returns 0 on success.
210 ) = parse_use_counters()
214 from mozbuild
.util
import FileAvoidWrite
216 yaml_path
= os
.path
.join(
217 buildconfig
.topsrcdir
, "dom", "base", "use_counter_metrics.yaml"
219 with
FileAvoidWrite(yaml_path
) as f
:
221 f
.write(BASE_METRICS
)
234 f
.write(f
"# Total of {total} use counter metrics (excludes denominators).\n")
235 f
.write(f
"# Total of {len(page)} 'page' use counters.\n")
236 f
.write("use.counter.page:\n")
237 for [_
, name
, desc
] in page
:
239 USE_COUNTER_TEMPLATE
.format(
242 denominator
="use.counter.top_level_content_documents_destroyed",
246 f
.write(f
"# Total of {len(doc)} 'document' use counters.\n")
247 f
.write("use.counter.doc:\n")
248 for [_
, name
, desc
] in doc
:
250 USE_COUNTER_TEMPLATE
.format(
253 denominator
="use.counter.content_documents_destroyed",
257 f
.write(f
"# Total of {len(dedicated)} 'dedicated worker' use counters.\n")
258 f
.write("use.counter.worker.dedicated:\n")
259 for [_
, name
, desc
] in dedicated
:
261 USE_COUNTER_TEMPLATE
.format(
264 denominator
="use.counter.dedicated_workers_destroyed",
268 f
.write(f
"# Total of {len(shared)} 'shared worker' use counters.\n")
269 f
.write("use.counter.worker.shared:\n")
270 for [_
, name
, desc
] in shared
:
272 USE_COUNTER_TEMPLATE
.format(
275 denominator
="use.counter.shared_workers_destroyed",
279 f
.write(f
"# Total of {len(service)} 'service worker' use counters.\n")
280 f
.write("use.counter.worker.service:\n")
281 for [_
, name
, desc
] in service
:
283 USE_COUNTER_TEMPLATE
.format(
286 denominator
="use.counter.service_workers_destroyed",
291 f
"# Total of {len(ops_page)} 'deprecated operations (page)' use counters.\n"
293 f
.write("use.counter.deprecated_ops.page:\n")
294 for [_
, name
, desc
] in ops_page
:
296 USE_COUNTER_TEMPLATE
.format(
299 denominator
="use.counter.top_level_content_documents_destroyed",
304 f
"# Total of {len(ops_doc)} 'deprecated operations (document)' use counters.\n"
306 f
.write("use.counter.deprecated_ops.doc:\n")
307 for [_
, name
, desc
] in ops_doc
:
309 USE_COUNTER_TEMPLATE
.format(
312 denominator
="use.counter.content_documents_destroyed",
316 f
.write(f
"# Total of {len(css_page)} 'CSS (page)' use counters.\n")
317 f
.write("use.counter.css.page:\n")
318 for [_
, name
, desc
] in css_page
:
320 USE_COUNTER_TEMPLATE
.format(
323 denominator
="use.counter.top_level_content_documents_destroyed",
327 f
.write(f
"# Total of {len(css_doc)} 'CSS (document)' use counters.\n")
328 f
.write("use.counter.css.doc:\n")
329 for [_
, name
, desc
] in css_doc
:
331 USE_COUNTER_TEMPLATE
.format(
334 denominator
="use.counter.content_documents_destroyed",
341 def parse_use_counters():
343 Finds use counters in:
344 * dom/base/UseCounters.conf
345 * dom/base/UseCountersWorker.conf
346 * dom/base/nsDeprecatedOperationsList.h
347 * !/layout/style/ServoCSSPropList.py
348 * servo/components/style/properties/counted_unknown_properties.py
349 and returns them as a tuple of lists of tuples of the form:
350 (page, doc, dedicated, shared, service, ops_page, ops_doc, css_page, css_doc)
351 where each of the items is a List<Tuple<enum_name, glean_name, description>>
352 where `enum_name` is the name of the enum variant from UseCounter.h
353 (like `eUseCounter_custom_CustomizedBuiltin`), and
354 where `glean_name` is the name conjugated to Glean metric name safety.
357 # Note, this function contains a duplication of enum naming logic from UseCounter.h.
358 # If you change the enum name format, you'll need to do it twice.
360 # There are 3 kinds of Use Counters in conf files: method, attribute, custom.
361 # `method` and `attribute` are presumed label-safe and are taken as-is.
362 # `custom` can be any case, so are coerced to snake_case.
367 uc_path
= os
.path
.join(buildconfig
.topsrcdir
, "dom", "base", "UseCounters.conf")
370 for counter
in read_conf(uc_path
):
371 if counter
["type"] == "method":
373 f
"eUseCounter_{counter['interface_name']}_{counter['method_name']}"
375 glean_name
= f
"{counter['interface_name']}_{counter['method_name']}".lower()
376 method
= f
"called {counter['interface_name']}.{counter['method_name']}"
377 page
.append((enum_name
, glean_name
, f
"Whether a page called {method}."))
378 doc
.append((enum_name
, glean_name
, f
"Whether a document called {method}."))
379 elif counter
["type"] == "attribute":
381 f
"eUseCounter_{counter['interface_name']}_{counter['attribute_name']}"
383 name
= f
"{counter['interface_name']}_{counter['attribute_name']}".lower()
384 attr
= f
"{counter['interface_name']}.{counter['attribute_name']}"
386 (f
"{enum_root}_getter", f
"{name}_getter", f
"Whether a page got {attr}.")
389 (f
"{enum_root}_setter", f
"{name}_setter", f
"Whether a page set {attr}.")
393 f
"{enum_root}_getter",
395 f
"Whether a document got {attr}.",
400 f
"{enum_root}_setter",
402 f
"Whether a document set {attr}.",
405 elif counter
["type"] == "custom":
406 enum_name
= f
"eUseCounter_custom_{counter['name']}"
410 to_snake_case(counter
["name"]),
411 f
"Whether a page {counter['desc']}.",
417 to_snake_case(counter
["name"]),
418 f
"Whether a document {counter['desc']}.",
422 print(f
"Found unexpected use counter type {counter['type']}. Returning 1.")
425 worker_uc_path
= os
.path
.join(
426 buildconfig
.topsrcdir
, "dom", "base", "UseCountersWorker.conf"
431 for counter
in read_conf(worker_uc_path
):
432 if counter
["type"] == "method":
433 enum_name
= f
"{counter['interface_name']}_{counter['method_name']}"
434 name
= f
"{counter['interface_name']}_{counter['method_name']}".lower()
435 method
= f
"called {counter['interface_name']}.{counter['method_name']}"
437 (enum_name
, name
, f
"Whether a dedicated worker called {method}.")
440 (enum_name
, name
, f
"Whether a shared worker called {method}.")
443 (enum_name
, name
, f
"Whether a service worker called {method}.")
445 elif counter
["type"] == "attribute":
446 enum_root
= f
"{counter['interface_name']}_{counter['attribute_name']}"
447 name
= f
"{counter['interface_name']}_{counter['attribute_name']}".lower()
448 attr
= f
"{counter['interface_name']}.{counter['attribute_name']}"
451 f
"{enum_root}_getter",
453 f
"Whether a dedicated worker got {attr}.",
458 f
"{enum_root}_setter",
460 f
"Whether a dedicated worker set {attr}.",
465 f
"{enum_root}_getter",
467 f
"Whether a shared worker got {attr}.",
472 f
"{enum_root}_setter",
474 f
"Whether a shared worker set {attr}.",
479 f
"{enum_root}_getter",
481 f
"Whether a service worker got {attr}.",
486 f
"{enum_root}_setter",
488 f
"Whether a service worker set {attr}.",
491 elif counter
["type"] == "custom":
492 enum_name
= f
"Custom_{counter['name']}"
496 to_snake_case(counter
["name"]),
497 f
"Whether a dedicated worker {counter['desc']}.",
503 to_snake_case(counter
["name"]),
504 f
"Whether a shared worker {counter['desc']}.",
510 to_snake_case(counter
["name"]),
511 f
"Whether a service worker {counter['desc']}.",
516 f
"Found unexpected worker use counter type {counter['type']}. Returning 1."
520 # nsDeprecatedOperationsList.h parsing is adapted from parse_histograms.py.
521 operation_list_path
= os
.path
.join(
522 buildconfig
.topsrcdir
, "dom", "base", "nsDeprecatedOperationList.h"
524 operation_regex
= re
.compile("^DEPRECATED_OPERATION\\(([^)]+)\\)")
527 with
open(operation_list_path
) as f
:
529 match
= operation_regex
.search(line
)
531 # No macro, probably whitespace or comment.
535 op_name
= to_snake_case(op
)
536 enum_name
= f
"eUseCounter_{op}"
537 ops_page
.append((enum_name
, op_name
, f
"Whether a page used {op}."))
538 ops_doc
.append((enum_name
, op_name
, f
"Whether a document used {op}."))
540 # Theoretically, we could do this without a completed build
541 # (ie, without the generated ServoCSSPropList.py) by sourcing direct from
542 # servo/components/style/properties/data.py:PropertiesData(engine=gecko).
544 # ...but parse_histograms.py doesn't do this the hard way. Should we?
548 proplist_path
= os
.path
.join(
549 buildconfig
.topobjdir
, "layout", "style", "ServoCSSPropList.py"
551 css_properties
= runpy
.run_path(proplist_path
)["data"]
554 for prop
in css_properties
.values():
555 # We prefix `prop_name` with `css_` to avoid colliding with C++ keywords
557 prop_name
= "css_" + to_snake_case(prop
.name
)
559 # Dependency keywords: CSS_PROP_PUBLIC_OR_PRIVATE, GenerateServoCSSPropList.py.
560 method
= "Float" if prop
.method
== "CssFloat" else prop
.method
561 # Dependency keywords: CSS_PROP_DOMPROP_PREFIXED, GenerateServoCSSPropList.py.
562 if method
.startswith("Moz") and prop
.type() != "alias":
563 method
= method
[3:] # remove the moz prefix
565 enum_name
= f
"eUseCounter_property_{method}"
567 (enum_name
, prop_name
, f
"Whether a page used the CSS property {prop.name}.")
573 f
"Whether a document used the CSS property {prop.name}.",
577 # Counted unknown properties: AKA - stuff that doesn't exist, but we want
578 # to count uses of anyway.
579 # We _might_ decide to implement these in the future, though, so we just add
580 # them to the css_page, css_doc lists directly for continuity.
581 # (We do give them a different description, though)
585 sys
.path
.append(os
.path
.join(buildconfig
.topsrcdir
, "layout", "style"))
586 from GenerateCountedUnknownProperties
import to_camel_case
588 unknown_proplist_path
= os
.path
.join(
589 buildconfig
.topsrcdir
,
594 "counted_unknown_properties.py",
596 unknown_properties
= runpy
.run_path(unknown_proplist_path
)[
597 "COUNTED_UNKNOWN_PROPERTIES"
599 for prop
in unknown_properties
:
600 enum_name
= f
"eUseCounter_unknown_property_{to_camel_case(prop)}"
601 prop_name
= to_snake_case(prop
)
606 f
"Whether a page used the (unknown, counted) CSS property {prop}.",
613 f
"Whether a document used the (unknown, counted) CSS property {prop}.",
617 return (page
, doc
, dedicated
, shared
, service
, ops_page
, ops_doc
, css_page
, css_doc
)
620 def to_snake_case(kebab_or_pascal
):
622 Takes `kebab_or_pascal` which is in PascalCase or kebab-case
623 and conjugates it to "snake_case" (all lowercase, "_"-delimited).
626 re
.sub("([A-Z]+)", r
"_\1", kebab_or_pascal
).replace("-", "_").lower().strip("_")
630 def metric_map(f
, *inputs
):
632 Parses all use counters and outputs UseCounter.cpp which contains implementations
633 for two functions defined in UseCounter.h:
634 * const char* IncrementUseCounter(UseCounter aUseCounter, bool aIsPage)
635 * const char* IncrementWorkerUseCounter(UseCounterWorker aUseCounter, dom::WorkerKind aKind)
637 (Basically big switch statements mapping from enums to glean metrics, calling Add())
650 ) = parse_use_counters()
654 /* AUTOGENERATED by usecounters.py. DO NOT EDIT */
655 /* This Source Code Form is subject to the terms of the Mozilla Public
656 * License, v. 2.0. If a copy of the MPL was not distributed with this
657 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
659 #include "mozilla/dom/UseCounterMetrics.h"
661 #include "mozilla/dom/WorkerPrivate.h"
662 #include "mozilla/glean/DomUseCounterMetrics.h"
664 namespace mozilla::dom {
666 const char* IncrementUseCounter(UseCounter aUseCounter, bool aIsPage) {
667 static constexpr struct {
669 glean::impl::CounterMetric doc_metric;
670 glean::impl::CounterMetric page_metric;
675 # This order must match the order UseCounter is defined,
676 # (and we guarantee it via the MOZ_ASSERT below at runtime).
677 assert len(page
) == len(doc
)
678 assert len(ops_page
) == len(ops_doc
)
679 assert len(css_page
) == len(css_doc
)
683 for pc
, dc
in zip(page
, doc
):
684 assert pc
[0] == dc
[0]
685 assert pc
[1] == dc
[1]
686 static_asserts
.append(f
"static_assert({index} == size_t(UseCounter::{pc[0]}));")
691 glean::use_counter_doc::{pc[1]},
692 glean::use_counter_page::{pc[1]},
698 for pc
, dc
in zip(ops_page
, ops_doc
):
699 assert pc
[0] == dc
[0]
700 assert pc
[1] == dc
[1]
701 static_asserts
.append(f
"static_assert({index} == size_t(UseCounter::{pc[0]}));")
705 "deprecated_ops.{pc[1]}",
706 glean::use_counter_deprecated_ops_doc::{pc[1]},
707 glean::use_counter_deprecated_ops_page::{pc[1]},
713 for pc
, dc
in zip(css_page
, css_doc
):
714 assert pc
[0] == dc
[0]
715 assert pc
[1] == dc
[1]
716 static_asserts
.append(f
"static_assert({index} == size_t(UseCounter::{pc[0]}));")
721 glean::use_counter_css_doc::{pc[1]},
722 glean::use_counter_css_page::{pc[1]},
729 f
.write("\n".join(static_asserts
))
732 MOZ_ASSERT(size_t(aUseCounter) < std::size(kEntries));
733 const auto& entry = kEntries[size_t(aUseCounter)];
734 (aIsPage ? entry.page_metric : entry.doc_metric).Add();
738 const char* IncrementWorkerUseCounter(UseCounterWorker aUseCounter, WorkerKind aKind) {
739 static constexpr struct {
741 glean::impl::CounterMetric dedicated_metric;
742 glean::impl::CounterMetric shared_metric;
743 glean::impl::CounterMetric service_metric;
747 assert len(dedicated
) == len(shared
)
748 assert len(dedicated
) == len(service
)
751 for dc
, sc
, servicec
in zip(dedicated
, shared
, service
):
752 assert dc
[0] == sc
[0]
753 assert dc
[1] == sc
[1]
754 assert dc
[0] == servicec
[0]
755 assert dc
[1] == servicec
[1]
756 static_asserts
.append(
757 f
"static_assert({index} == size_t(UseCounterWorker::{dc[0]}));"
763 glean::use_counter_worker_dedicated::{dc[1]},
764 glean::use_counter_worker_shared::{dc[1]},
765 glean::use_counter_worker_service::{dc[1]},
771 f
.write("\n".join(static_asserts
))
774 MOZ_ASSERT(size_t(aUseCounter) < std::size(kEntries));
775 const auto& entry = kEntries[size_t(aUseCounter)];
777 case WorkerKind::WorkerKindDedicated:
778 entry.dedicated_metric.Add();
780 case WorkerKind::WorkerKindShared:
781 entry.shared_metric.Add();
783 case WorkerKind::WorkerKindService:
784 entry.service_metric.Add();
790 } // namespace mozilla