usecounters.py (26121B)
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/. 4 5 import re 6 7 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) 11 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 17 continue 18 m = re.match(r"method ([A-Za-z0-9]+)\.([A-Za-z0-9]+)$", line) 19 if m: 20 interface_name, method_name = m.groups() 21 yield { 22 "type": "method", 23 "interface_name": interface_name, 24 "method_name": method_name, 25 } 26 continue 27 m = re.match(r"attribute ([A-Za-z0-9]+)\.([A-Za-z0-9]+)$", line) 28 if m: 29 interface_name, attribute_name = m.groups() 30 yield { 31 "type": "attribute", 32 "interface_name": interface_name, 33 "attribute_name": attribute_name, 34 } 35 continue 36 m = re.match(r"custom ([A-Za-z0-9_]+) (.*)$", line) 37 if m: 38 name, desc = m.groups() 39 yield {"type": "custom", "name": name, "desc": desc} 40 continue 41 raise ValueError( 42 "error parsing %s at line %d" % (conf_filename, line_num + 1) 43 ) 44 45 return parse_counters(stream) 46 47 48 YAML_HEADER = """\ 49 # This file is AUTOGENERATED by usecounters.py. DO NOT EDIT. 50 # (instead, re-run ./mach gen-use-counter-metrics) 51 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/. 55 56 --- 57 $schema: moz://mozilla.org/schemas/glean/metrics/2-0-0 58 $tags: 59 - 'Core :: DOM: Core & HTML' 60 61 """ 62 63 BASE_METRICS = """\ 64 use.counter: 65 content_documents_destroyed: 66 type: counter 67 description: > 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`). 72 bugs: 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 77 data_reviews: 78 - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672 79 notification_emails: 80 - dom-core@mozilla.com 81 - emilio@mozilla.com 82 expires: never 83 send_in_pings: 84 - use-counters 85 86 top_level_content_documents_destroyed: 87 type: counter 88 description: > 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`). 93 bugs: 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 98 data_reviews: 99 - https://bugzilla.mozilla.org/show_bug.cgi?id=1569672 100 notification_emails: 101 - dom-core@mozilla.com 102 - emilio@mozilla.com 103 expires: never 104 send_in_pings: 105 - use-counters 106 - metrics 107 108 dedicated_workers_destroyed: 109 type: counter 110 description: > 111 A count of how many `Dedicated`-kind workers were destroyed. 112 Used to turn dedicated worker use counters' counts into rates. 113 Excludes chrome workers. 114 bugs: 115 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706 116 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 117 data_reviews: 118 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706 119 notification_emails: 120 - dom-core@mozilla.com 121 - emilio@mozilla.com 122 expires: never 123 send_in_pings: 124 - use-counters 125 126 shared_workers_destroyed: 127 type: counter 128 description: > 129 A count of how many `Shared`-kind workers were destroyed. 130 Used to turn shared worker use counters' counts into rates. 131 Excludes chrome workers. 132 bugs: 133 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706 134 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 135 data_reviews: 136 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706 137 notification_emails: 138 - dom-core@mozilla.com 139 - emilio@mozilla.com 140 expires: never 141 send_in_pings: 142 - use-counters 143 144 service_workers_destroyed: 145 type: counter 146 description: > 147 A count of how many `Service`-kind workers were destroyed. 148 Used to turn service worker use counters' counts into rates. 149 Excludes chrome workers. 150 bugs: 151 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706 152 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 153 data_reviews: 154 - https://bugzilla.mozilla.org/show_bug.cgi?id=1202706 155 notification_emails: 156 - dom-core@mozilla.com 157 - emilio@mozilla.com 158 expires: never 159 send_in_pings: 160 - use-counters 161 162 """ 163 164 USE_COUNTER_TEMPLATE = """\ 165 {name}: 166 type: counter 167 description: > 168 {desc} 169 Compare against `{denominator}` 170 to calculate the rate. 171 bugs: 172 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 173 data_reviews: 174 - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098 175 notification_emails: 176 - dom-core@mozilla.com 177 - emilio@mozilla.com 178 expires: never 179 send_in_pings: 180 - use-counters 181 182 """ 183 184 185 def gen_use_counter_metrics(): 186 """ 187 Finds use counters in: 188 * dom/base/UseCounters.conf 189 * dom/base/UseCountersWorker.conf 190 * dom/base/nsDeprecatedOperationsList.h 191 * !/layout/style/ServoCSSPropList.py 192 * servo/components/style/properties/counted_unknown_properties.py 193 and overwrites the Glean metrics definition file 194 `dom/base/use_counter_metrics.yaml` with definitions for each use counter found. 195 196 IF YOU CHANGE THIS FUNCTION: 197 * You should probably add your bug's number to USE_COUNTER_TEMPLATE, above. 198 199 Returns 0 on success. 200 """ 201 ( 202 page, 203 doc, 204 dedicated, 205 shared, 206 service, 207 ops_page, 208 ops_doc, 209 css_page, 210 css_doc, 211 ) = parse_use_counters() 212 import os 213 214 import buildconfig 215 from mozbuild.util import FileAvoidWrite 216 217 yaml_path = os.path.join( 218 buildconfig.topsrcdir, "dom", "base", "use_counter_metrics.yaml" 219 ) 220 with FileAvoidWrite(yaml_path) as f: 221 f.write(YAML_HEADER) 222 f.write(BASE_METRICS) 223 224 total = ( 225 len(page) 226 + len(doc) 227 + len(dedicated) 228 + len(shared) 229 + len(service) 230 + len(ops_page) 231 + len(ops_doc) 232 + len(css_page) 233 + len(css_doc) 234 ) 235 f.write(f"# Total of {total} use counter metrics (excludes denominators).\n") 236 f.write(f"# Total of {len(page)} 'page' use counters.\n") 237 f.write("use.counter.page:\n") 238 for [_, name, desc] in page: 239 f.write( 240 USE_COUNTER_TEMPLATE.format( 241 name=name, 242 desc=desc, 243 denominator="use.counter.top_level_content_documents_destroyed", 244 ) 245 ) 246 247 f.write(f"# Total of {len(doc)} 'document' use counters.\n") 248 f.write("use.counter.doc:\n") 249 for [_, name, desc] in doc: 250 f.write( 251 USE_COUNTER_TEMPLATE.format( 252 name=name, 253 desc=desc, 254 denominator="use.counter.content_documents_destroyed", 255 ) 256 ) 257 258 f.write(f"# Total of {len(dedicated)} 'dedicated worker' use counters.\n") 259 f.write("use.counter.worker.dedicated:\n") 260 for [_, name, desc] in dedicated: 261 f.write( 262 USE_COUNTER_TEMPLATE.format( 263 name=name, 264 desc=desc, 265 denominator="use.counter.dedicated_workers_destroyed", 266 ) 267 ) 268 269 f.write(f"# Total of {len(shared)} 'shared worker' use counters.\n") 270 f.write("use.counter.worker.shared:\n") 271 for [_, name, desc] in shared: 272 f.write( 273 USE_COUNTER_TEMPLATE.format( 274 name=name, 275 desc=desc, 276 denominator="use.counter.shared_workers_destroyed", 277 ) 278 ) 279 280 f.write(f"# Total of {len(service)} 'service worker' use counters.\n") 281 f.write("use.counter.worker.service:\n") 282 for [_, name, desc] in service: 283 f.write( 284 USE_COUNTER_TEMPLATE.format( 285 name=name, 286 desc=desc, 287 denominator="use.counter.service_workers_destroyed", 288 ) 289 ) 290 291 f.write( 292 f"# Total of {len(ops_page)} 'deprecated operations (page)' use counters.\n" 293 ) 294 f.write("use.counter.deprecated_ops.page:\n") 295 for [_, name, desc] in ops_page: 296 f.write( 297 USE_COUNTER_TEMPLATE.format( 298 name=name, 299 desc=desc, 300 denominator="use.counter.top_level_content_documents_destroyed", 301 ) 302 ) 303 304 f.write( 305 f"# Total of {len(ops_doc)} 'deprecated operations (document)' use counters.\n" 306 ) 307 f.write("use.counter.deprecated_ops.doc:\n") 308 for [_, name, desc] in ops_doc: 309 f.write( 310 USE_COUNTER_TEMPLATE.format( 311 name=name, 312 desc=desc, 313 denominator="use.counter.content_documents_destroyed", 314 ) 315 ) 316 317 f.write(f"# Total of {len(css_page)} 'CSS (page)' use counters.\n") 318 f.write("use.counter.css.page:\n") 319 for [_, name, desc] in css_page: 320 f.write( 321 USE_COUNTER_TEMPLATE.format( 322 name=name, 323 desc=desc, 324 denominator="use.counter.top_level_content_documents_destroyed", 325 ) 326 ) 327 328 f.write(f"# Total of {len(css_doc)} 'CSS (document)' use counters.\n") 329 f.write("use.counter.css.doc:\n") 330 for [_, name, desc] in css_doc: 331 f.write( 332 USE_COUNTER_TEMPLATE.format( 333 name=name, 334 desc=desc, 335 denominator="use.counter.content_documents_destroyed", 336 ) 337 ) 338 339 return 0 340 341 342 def parse_use_counters(): 343 """ 344 Finds use counters in: 345 * dom/base/UseCounters.conf 346 * dom/base/UseCountersWorker.conf 347 * dom/base/nsDeprecatedOperationsList.h 348 * !/layout/style/ServoCSSPropList.py 349 * servo/components/style/properties/counted_unknown_properties.py 350 and returns them as a tuple of lists of tuples of the form: 351 (page, doc, dedicated, shared, service, ops_page, ops_doc, css_page, css_doc) 352 where each of the items is a List<Tuple<enum_name, glean_name, description>> 353 where `enum_name` is the name of the enum variant from UseCounter.h 354 (like `eUseCounter_custom_CustomizedBuiltin`), and 355 where `glean_name` is the name conjugated to Glean metric name safety. 356 """ 357 358 # Note, this function contains a duplication of enum naming logic from UseCounter.h. 359 # If you change the enum name format, you'll need to do it twice. 360 361 # There are 3 kinds of Use Counters in conf files: method, attribute, custom. 362 # `method` and `attribute` are presumed label-safe and are taken as-is. 363 # `custom` can be any case, so are coerced to snake_case. 364 import os 365 366 import buildconfig 367 368 uc_path = os.path.join(buildconfig.topsrcdir, "dom", "base", "UseCounters.conf") 369 page = [] 370 doc = [] 371 for counter in read_conf(uc_path): 372 if counter["type"] == "method": 373 enum_name = ( 374 f"eUseCounter_{counter['interface_name']}_{counter['method_name']}" 375 ) 376 glean_name = f"{counter['interface_name']}_{counter['method_name']}".lower() 377 method = f"called {counter['interface_name']}.{counter['method_name']}" 378 page.append((enum_name, glean_name, f"Whether a page called {method}.")) 379 doc.append((enum_name, glean_name, f"Whether a document called {method}.")) 380 elif counter["type"] == "attribute": 381 enum_root = ( 382 f"eUseCounter_{counter['interface_name']}_{counter['attribute_name']}" 383 ) 384 name = f"{counter['interface_name']}_{counter['attribute_name']}".lower() 385 attr = f"{counter['interface_name']}.{counter['attribute_name']}" 386 page.append(( 387 f"{enum_root}_getter", 388 f"{name}_getter", 389 f"Whether a page got {attr}.", 390 )) 391 page.append(( 392 f"{enum_root}_setter", 393 f"{name}_setter", 394 f"Whether a page set {attr}.", 395 )) 396 doc.append(( 397 f"{enum_root}_getter", 398 f"{name}_getter", 399 f"Whether a document got {attr}.", 400 )) 401 doc.append(( 402 f"{enum_root}_setter", 403 f"{name}_setter", 404 f"Whether a document set {attr}.", 405 )) 406 elif counter["type"] == "custom": 407 enum_name = f"eUseCounter_custom_{counter['name']}" 408 page.append(( 409 enum_name, 410 to_snake_case(counter["name"]), 411 f"Whether a page {counter['desc']}.", 412 )) 413 doc.append(( 414 enum_name, 415 to_snake_case(counter["name"]), 416 f"Whether a document {counter['desc']}.", 417 )) 418 else: 419 print(f"Found unexpected use counter type {counter['type']}. Returning 1.") 420 return 1 421 422 worker_uc_path = os.path.join( 423 buildconfig.topsrcdir, "dom", "base", "UseCountersWorker.conf" 424 ) 425 dedicated = [] 426 shared = [] 427 service = [] 428 for counter in read_conf(worker_uc_path): 429 if counter["type"] == "method": 430 enum_name = f"{counter['interface_name']}_{counter['method_name']}" 431 name = f"{counter['interface_name']}_{counter['method_name']}".lower() 432 method = f"called {counter['interface_name']}.{counter['method_name']}" 433 dedicated.append(( 434 enum_name, 435 name, 436 f"Whether a dedicated worker called {method}.", 437 )) 438 shared.append(( 439 enum_name, 440 name, 441 f"Whether a shared worker called {method}.", 442 )) 443 service.append(( 444 enum_name, 445 name, 446 f"Whether a service worker called {method}.", 447 )) 448 elif counter["type"] == "attribute": 449 enum_root = f"{counter['interface_name']}_{counter['attribute_name']}" 450 name = f"{counter['interface_name']}_{counter['attribute_name']}".lower() 451 attr = f"{counter['interface_name']}.{counter['attribute_name']}" 452 dedicated.append(( 453 f"{enum_root}_getter", 454 f"{name}_getter", 455 f"Whether a dedicated worker got {attr}.", 456 )) 457 dedicated.append(( 458 f"{enum_root}_setter", 459 f"{name}_setter", 460 f"Whether a dedicated worker set {attr}.", 461 )) 462 shared.append(( 463 f"{enum_root}_getter", 464 f"{name}_getter", 465 f"Whether a shared worker got {attr}.", 466 )) 467 shared.append(( 468 f"{enum_root}_setter", 469 f"{name}_setter", 470 f"Whether a shared worker set {attr}.", 471 )) 472 service.append(( 473 f"{enum_root}_getter", 474 f"{name}_getter", 475 f"Whether a service worker got {attr}.", 476 )) 477 service.append(( 478 f"{enum_root}_setter", 479 f"{name}_setter", 480 f"Whether a service worker set {attr}.", 481 )) 482 elif counter["type"] == "custom": 483 enum_name = f"Custom_{counter['name']}" 484 dedicated.append(( 485 enum_name, 486 to_snake_case(counter["name"]), 487 f"Whether a dedicated worker {counter['desc']}.", 488 )) 489 shared.append(( 490 enum_name, 491 to_snake_case(counter["name"]), 492 f"Whether a shared worker {counter['desc']}.", 493 )) 494 service.append(( 495 enum_name, 496 to_snake_case(counter["name"]), 497 f"Whether a service worker {counter['desc']}.", 498 )) 499 else: 500 print( 501 f"Found unexpected worker use counter type {counter['type']}. Returning 1." 502 ) 503 return 1 504 505 # nsDeprecatedOperationsList.h parsing is adapted from parse_histograms.py. 506 operation_list_path = os.path.join( 507 buildconfig.topsrcdir, "dom", "base", "nsDeprecatedOperationList.h" 508 ) 509 operation_regex = re.compile("^DEPRECATED_OPERATION\\(([^)]+)\\)") 510 ops_page = [] 511 ops_doc = [] 512 with open(operation_list_path) as f: 513 for line in f: 514 match = operation_regex.search(line) 515 if not match: 516 # No macro, probably whitespace or comment. 517 continue 518 519 op = match.group(1) 520 op_name = to_snake_case(op) 521 enum_name = f"eUseCounter_{op}" 522 ops_page.append((enum_name, op_name, f"Whether a page used {op}.")) 523 ops_doc.append((enum_name, op_name, f"Whether a document used {op}.")) 524 525 # Theoretically, we could do this without a completed build 526 # (ie, without the generated ServoCSSPropList.py) by sourcing direct from 527 # servo/components/style/properties/data.py:PropertiesData(engine=gecko). 528 # 529 # ...but parse_histograms.py doesn't do this the hard way. Should we? 530 531 import runpy 532 533 proplist_path = os.path.join( 534 buildconfig.topobjdir, "layout", "style", "ServoCSSPropList.py" 535 ) 536 css_properties = runpy.run_path(proplist_path)["data"] 537 css_page = [] 538 css_doc = [] 539 for prop in css_properties.values(): 540 # We prefix `prop_name` with `css_` to avoid colliding with C++ keywords 541 # like `float`. 542 prop_name = "css_" + to_snake_case(prop.name) 543 544 # Dependency keywords: CSS_PROP_PUBLIC_OR_PRIVATE, GenerateServoCSSPropList.py. 545 method = "Float" if prop.method == "CssFloat" else prop.method 546 # Dependency keywords: CSS_PROP_DOMPROP_PREFIXED, GenerateServoCSSPropList.py. 547 if method.startswith("Moz") and prop.type() != "alias": 548 method = method[3:] # remove the moz prefix 549 550 enum_name = f"eUseCounter_property_{method}" 551 css_page.append(( 552 enum_name, 553 prop_name, 554 f"Whether a page used the CSS property {prop.name}.", 555 )) 556 css_doc.append(( 557 enum_name, 558 prop_name, 559 f"Whether a document used the CSS property {prop.name}.", 560 )) 561 562 # Counted unknown properties: AKA - stuff that doesn't exist, but we want 563 # to count uses of anyway. 564 # We _might_ decide to implement these in the future, though, so we just add 565 # them to the css_page, css_doc lists directly for continuity. 566 # (We do give them a different description, though) 567 568 import sys 569 570 sys.path.append(os.path.join(buildconfig.topsrcdir, "layout", "style")) 571 from GenerateCountedUnknownProperties import to_camel_case 572 573 unknown_proplist_path = os.path.join( 574 buildconfig.topsrcdir, 575 "servo", 576 "components", 577 "style", 578 "properties", 579 "counted_unknown_properties.py", 580 ) 581 unknown_properties = runpy.run_path(unknown_proplist_path)[ 582 "COUNTED_UNKNOWN_PROPERTIES" 583 ] 584 for prop in unknown_properties: 585 enum_name = f"eUseCounter_unknown_property_{to_camel_case(prop)}" 586 prop_name = to_snake_case(prop) 587 css_page.append(( 588 enum_name, 589 prop_name, 590 f"Whether a page used the (unknown, counted) CSS property {prop}.", 591 )) 592 css_doc.append(( 593 enum_name, 594 prop_name, 595 f"Whether a document used the (unknown, counted) CSS property {prop}.", 596 )) 597 598 return (page, doc, dedicated, shared, service, ops_page, ops_doc, css_page, css_doc) 599 600 601 def to_snake_case(kebab_or_pascal): 602 """ 603 Takes `kebab_or_pascal` which is in PascalCase or kebab-case 604 and conjugates it to "snake_case" (all lowercase, "_"-delimited). 605 """ 606 return ( 607 re.sub("([A-Z]+)", r"_\1", kebab_or_pascal).replace("-", "_").lower().strip("_") 608 ) 609 610 611 def metric_map(f, *inputs): 612 """ 613 Parses all use counters and outputs UseCounter.cpp which contains implementations 614 for two functions defined in UseCounter.h: 615 * const char* IncrementUseCounter(UseCounter aUseCounter, bool aIsPage) 616 * const char* IncrementWorkerUseCounter(UseCounterWorker aUseCounter, dom::WorkerKind aKind) 617 618 (Basically big switch statements mapping from enums to glean metrics, calling Add()) 619 """ 620 621 ( 622 page, 623 doc, 624 dedicated, 625 shared, 626 service, 627 ops_page, 628 ops_doc, 629 css_page, 630 css_doc, 631 ) = parse_use_counters() 632 633 f.write( 634 """\ 635 /* AUTOGENERATED by usecounters.py. DO NOT EDIT */ 636 /* This Source Code Form is subject to the terms of the Mozilla Public 637 * License, v. 2.0. If a copy of the MPL was not distributed with this 638 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 639 640 #include "mozilla/dom/UseCounterMetrics.h" 641 642 #include "mozilla/dom/WorkerPrivate.h" 643 #include "mozilla/glean/DomUseCounterMetrics.h" 644 645 namespace mozilla::dom { 646 647 const char* IncrementUseCounter(UseCounter aUseCounter, bool aIsPage) { 648 static constexpr struct { 649 const char* name; 650 glean::impl::CounterMetric<> doc_metric; 651 glean::impl::CounterMetric<> page_metric; 652 } kEntries[] = { 653 """ 654 ) 655 656 # This order must match the order UseCounter is defined, 657 # (and we guarantee it via the MOZ_ASSERT below at runtime). 658 assert len(page) == len(doc) 659 assert len(ops_page) == len(ops_doc) 660 assert len(css_page) == len(css_doc) 661 662 index = 0 663 static_asserts = [] 664 for pc, dc in zip(page, doc): 665 assert pc[0] == dc[0] 666 assert pc[1] == dc[1] 667 static_asserts.append(f"static_assert({index} == size_t(UseCounter::{pc[0]}));") 668 f.write( 669 f"""\ 670 {{ 671 "{pc[1]}", 672 glean::use_counter_doc::{pc[1]}, 673 glean::use_counter_page::{pc[1]}, 674 }}, 675 """ 676 ) 677 index += 1 678 679 for pc, dc in zip(ops_page, ops_doc): 680 assert pc[0] == dc[0] 681 assert pc[1] == dc[1] 682 static_asserts.append(f"static_assert({index} == size_t(UseCounter::{pc[0]}));") 683 f.write( 684 f"""\ 685 {{ 686 "deprecated_ops.{pc[1]}", 687 glean::use_counter_deprecated_ops_doc::{pc[1]}, 688 glean::use_counter_deprecated_ops_page::{pc[1]}, 689 }}, 690 """ 691 ) 692 index += 1 693 694 for pc, dc in zip(css_page, css_doc): 695 assert pc[0] == dc[0] 696 assert pc[1] == dc[1] 697 static_asserts.append(f"static_assert({index} == size_t(UseCounter::{pc[0]}));") 698 f.write( 699 f"""\ 700 {{ 701 "css.{pc[1]}", 702 glean::use_counter_css_doc::{pc[1]}, 703 glean::use_counter_css_page::{pc[1]}, 704 }}, 705 """ 706 ) 707 index += 1 708 709 f.write("};\n") 710 f.write("\n".join(static_asserts)) 711 f.write( 712 """\ 713 MOZ_ASSERT(size_t(aUseCounter) < std::size(kEntries)); 714 const auto& entry = kEntries[size_t(aUseCounter)]; 715 (aIsPage ? entry.page_metric : entry.doc_metric).Add(); 716 return entry.name; 717 } 718 719 const char* IncrementWorkerUseCounter(UseCounterWorker aUseCounter, WorkerKind aKind) { 720 static constexpr struct { 721 const char* name; 722 glean::impl::CounterMetric<> dedicated_metric; 723 glean::impl::CounterMetric<> shared_metric; 724 glean::impl::CounterMetric<> service_metric; 725 } kEntries[] = { 726 """ 727 ) 728 assert len(dedicated) == len(shared) 729 assert len(dedicated) == len(service) 730 index = 0 731 static_asserts = [] 732 for dc, sc, servicec in zip(dedicated, shared, service): 733 assert dc[0] == sc[0] 734 assert dc[1] == sc[1] 735 assert dc[0] == servicec[0] 736 assert dc[1] == servicec[1] 737 static_asserts.append( 738 f"static_assert({index} == size_t(UseCounterWorker::{dc[0]}));" 739 ) 740 f.write( 741 f"""\ 742 {{ 743 "{dc[1]}", 744 glean::use_counter_worker_dedicated::{dc[1]}, 745 glean::use_counter_worker_shared::{dc[1]}, 746 glean::use_counter_worker_service::{dc[1]}, 747 }}, 748 """ 749 ) 750 index += 1 751 f.write("};\n") 752 f.write("\n".join(static_asserts)) 753 f.write( 754 """\ 755 MOZ_ASSERT(size_t(aUseCounter) < std::size(kEntries)); 756 const auto& entry = kEntries[size_t(aUseCounter)]; 757 switch (aKind) { 758 case WorkerKind::WorkerKindDedicated: 759 entry.dedicated_metric.Add(); 760 break; 761 case WorkerKind::WorkerKindShared: 762 entry.shared_metric.Add(); 763 break; 764 case WorkerKind::WorkerKindService: 765 entry.service_metric.Add(); 766 break; 767 } 768 return entry.name; 769 } 770 771 } // namespace mozilla 772 """ 773 )