diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /mozglue/baseprofiler/build | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mozglue/baseprofiler/build')
-rw-r--r-- | mozglue/baseprofiler/build/generate_profiling_categories.py | 350 | ||||
-rw-r--r-- | mozglue/baseprofiler/build/profiling_categories.yaml | 301 |
2 files changed, 651 insertions, 0 deletions
diff --git a/mozglue/baseprofiler/build/generate_profiling_categories.py b/mozglue/baseprofiler/build/generate_profiling_categories.py new file mode 100644 index 0000000000..64da056bbd --- /dev/null +++ b/mozglue/baseprofiler/build/generate_profiling_categories.py @@ -0,0 +1,350 @@ +# 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/. + +# This script generates ProfilingCategoryList.h and profiling_categories.rs +# files from profiling_categories.yaml. + +from collections import OrderedDict + +import yaml + +CPP_HEADER_TEMPLATE = """\ +/* 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/. */ + +#ifndef {includeguard} +#define {includeguard} + +/* This file is generated by generate_profiling_categories.py from + profiling_categories.yaml. DO NOT EDIT! */ + +// Profiler sub-categories are applied to each sampled stack to describe the +// type of workload that the CPU is busy with. Only one sub-category can be +// assigned so be mindful that these are non-overlapping. The active category is +// set by pushing a label to the profiling stack, or by the unwinder in cases +// such as JITs. A profile sample in arbitrary C++/Rust will typically be +// categorized based on the top of the label stack. +// +// The list of available color names for categories is: +// transparent +// blue +// green +// grey +// lightblue +// magenta +// orange +// purple +// yellow + +// clang-format off + +{contents} + +// clang-format on + +#endif // {includeguard} +""" + +CPP_MACRO_DEFINITION = """\ +#define MOZ_PROFILING_CATEGORY_LIST(BEGIN_CATEGORY, SUBCATEGORY, END_CATEGORY) \\ +""" + +RUST_TEMPLATE = """\ +/* 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/. */ + +/* This file is generated by generate_profiling_categories.py from + profiling_categories.yaml. DO NOT EDIT! */ + +{contents}\ +""" + +RUST_ENUM_TEMPLATE = """\ +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +pub enum {name} {{ +{fields} +}} +""" + +RUST_CONVERSION_IMPL_TEMPLATE = """\ +impl {name} {{ + pub fn to_cpp_enum_value(&self) -> u32 {{ +{content} + }} +}} +""" + +RUST_DEFAULT_IMPL_TEMPLATE = """\ +impl Default for {name} {{ + fn default() -> Self {{ +{content} + }} +}} +""" + +RUST_MATCH_SELF = """\ + match *self {{ +{fields} + }} +""" + + +def generate_header(c_out, includeguard, contents): + c_out.write( + CPP_HEADER_TEMPLATE.format(includeguard=includeguard, contents=contents) + ) + + +def generate_rust_file(c_out, contents): + c_out.write(RUST_TEMPLATE.format(contents=contents)) + + +def load_yaml(yaml_path): + # Load into an OrderedDict to ensure order is preserved. Note: Python 3.7+ + # also preserves ordering for normal dictionaries. + # Code based on https://stackoverflow.com/a/21912744. + class OrderedLoader(yaml.Loader): + pass + + def construct_mapping(loader, node): + loader.flatten_mapping(node) + return OrderedDict(loader.construct_pairs(node)) + + tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG + OrderedLoader.add_constructor(tag, construct_mapping) + + file_handler = open(yaml_path) + return yaml.load(file_handler, OrderedLoader) + + +def generate_category_macro(name, label, color, subcategories): + contents = ' BEGIN_CATEGORY({name}, "{label}", "{color}") \\\n'.format( + name=name, label=label, color=color + ) + + subcategory_items = [] + + for subcategory in subcategories: + subcat_name = subcategory["name"] + assert isinstance(subcat_name, str) + subcat_label = subcategory["label"] + assert isinstance(subcat_label, str) + + subcategory_items.append( + ' SUBCATEGORY({parent_cat}, {name}, "{label}") \\\n'.format( + parent_cat=name, name=subcat_name, label=subcat_label + ) + ) + + contents += "".join(subcategory_items) + contents += " END_CATEGORY" + + return contents + + +def generate_macro_header(c_out, yaml_path): + """Generate ProfilingCategoryList.h from profiling_categories.yaml. + The generated file has a macro to generate the profiling category enums. + """ + + data = load_yaml(yaml_path) + + # Stores the macro definition of each categories. + category_items = [] + + for category in data: + name = category["name"] + assert isinstance(name, str) + label = category["label"] + assert isinstance(label, str) + color = category["color"] + assert isinstance(color, str) + subcategories = category.get("subcategories", None) + assert ( + isinstance(subcategories, list) and len(subcategories) > 0 + ), "At least one subcategory expected as default in {}.".format(name) + + category_items.append( + generate_category_macro(name, label, color, subcategories) + ) + + contents = CPP_MACRO_DEFINITION + contents += " \\\n".join(category_items) + + generate_header(c_out, "baseprofiler_ProfilingCategoryList_h", contents) + + +class RustEnum: + """Class that keeps the rust enum fields and impls. + This is used for generating the Rust ProfilingCategoryPair and ProfilingCategory + enums as well as ProfilingCategoryPair's sub category enums. + For example, this can either generate an enum with discrimant fields for sub + category enums and ProfilingCategory: + ``` + #[repr(u32)] + #[derive(Debug, Copy, Clone)] + pub enum Graphics { + LayerBuilding = 0, + ... + } + ``` + or can generate an enum with optional tuple values for ProfilingCategoryPair + to explicitly mention their sub categories: + ``` + #[repr(u32)] + #[derive(Debug, Copy, Clone)] + pub enum ProfilingCategoryPair { + Network(Option<Network>), + ... + } + ``` + + And in addition to enums, it will generate impls for each enum. See one + example below: + ``` + impl Default for Network { + fn default() -> Self { + Network::Other + } + } + ``` + """ + + def __init__(self, name): + # Name of the Rust enum. + self.name = name + # Fields of the Rust enum. This list contains elements of + # (field_name, field_string) tuple for convenience. + self.fields = [] + # Impls of the Rust enum. Each element is a string. + self.impls = [] + # Default category of the Rust enum. Main enums won't have it, but all + # sub category enums must have one. This is being checked later. + self.default_category = None + + def append_optional_tuple_field(self, field_name): + """Append the enum fields list with an optional tuple field.""" + field = (field_name, " {name}(Option<{name}>),".format(name=field_name)) + self.fields.append(field) + + def append_discriminant_field(self, field_name, field_value): + """Append the enum fields list with a discriminant field.""" + field = ( + field_name, + " {name} = {value},".format(name=field_name, value=field_value), + ) + self.fields.append(field) + + def append_default_impl(self, default_category): + """Append the enum impls list with a default implementation.""" + self.default_category = default_category + + self.impls.append( + RUST_DEFAULT_IMPL_TEMPLATE.format( + name=self.name, + content=" {category}::{subcategory}".format( + category=self.name, subcategory=self.default_category + ), + ) + ) + + def append_conversion_impl(self, content): + """Append the enum impls list with a conversion implementation for cpp values.""" + self.impls.append( + RUST_CONVERSION_IMPL_TEMPLATE.format(name=self.name, content=content) + ) + + def to_rust_string(self): + """Serialize the enum with its impls as a string""" + joined_fields = "\n".join(map(lambda field: field[1], self.fields)) + result = RUST_ENUM_TEMPLATE.format(name=self.name, fields=joined_fields) + result += "\n" + result += "\n".join(self.impls) + return result + + +def generate_rust_enums(c_out, yaml_path): + """Generate profiling_categories.rs from profiling_categories.yaml. + The generated file has a profiling category enums and their impls. + """ + + data = load_yaml(yaml_path) + + # Each category has its own enum for keeping its subcategories. We are + # keeping all of them here. + enums = [] + # Parent enums for prifiling category and profiling category pair. They will + # be appended to the end of the `enums`. + profiling_category_pair_enum = RustEnum("ProfilingCategoryPair") + profiling_category_enum = RustEnum("ProfilingCategory") + profiling_category_pair_value = 0 + + for cat_index, category in enumerate(data): + cat_name = category["name"] + assert isinstance(cat_name, str) + cat_label = category["label"] + assert isinstance(cat_label, str) + # This will be used as our main enum field and sub category enum. + cat_label = "".join(filter(str.isalnum, cat_label)) + cat_subcategories = category.get("subcategories", None) + assert ( + isinstance(cat_subcategories, list) and len(cat_subcategories) > 0 + ), "At least one subcategory expected as default in {}.".format(cat_name) + + # Create a new enum for this sub category and append it to the enums list. + category_enum = RustEnum(cat_label) + enums.append(category_enum) + + for subcategory in cat_subcategories: + subcat_name = subcategory["name"] + assert isinstance(subcat_name, str) + subcat_label = subcategory["label"] + assert isinstance(subcat_label, str) + friendly_subcat_name = None + + if cat_name == subcat_name: + # This is the default sub-category. It should use the label as name. + friendly_subcat_name = subcat_label + category_enum.append_default_impl(subcat_label) + else: + # This is a non-default sub-category. + underscore_pos = subcat_name.find("_") + friendly_subcat_name = subcat_name[underscore_pos + 1 :] + + friendly_subcat_name = "".join(filter(str.isalnum, friendly_subcat_name)) + category_enum.append_discriminant_field( + friendly_subcat_name, profiling_category_pair_value + ) + profiling_category_pair_value += 1 + + assert ( + category_enum.default_category is not None + ), "There must be a default subcategory with the same name." + + # Append the main enums. + profiling_category_pair_enum.append_optional_tuple_field(cat_label) + profiling_category_enum.append_discriminant_field(cat_label, cat_index) + + # Add the main enums impls for conversion into cpp values. + profiling_category_pair_impl_fields = "\n".join( + " {enum_name}::{field_name}(val) => val.unwrap_or_default() as u32,".format( + enum_name="ProfilingCategoryPair", field_name=field + ) + for field, _ in profiling_category_pair_enum.fields + ) + profiling_category_pair_enum.append_conversion_impl( + RUST_MATCH_SELF.format(fields=profiling_category_pair_impl_fields) + ) + profiling_category_enum.append_conversion_impl(" *self as u32") + + # After adding all the sub category enums, we can add the main enums to the list. + enums.append(profiling_category_pair_enum) + enums.append(profiling_category_enum) + + # Print all the enums and their impls. + contents = "\n".join(map(lambda enum: enum.to_rust_string(), enums)) + generate_rust_file(c_out, contents) diff --git a/mozglue/baseprofiler/build/profiling_categories.yaml b/mozglue/baseprofiler/build/profiling_categories.yaml new file mode 100644 index 0000000000..62fd5d0617 --- /dev/null +++ b/mozglue/baseprofiler/build/profiling_categories.yaml @@ -0,0 +1,301 @@ +# 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/. + +# Profiling categories +# ==================== +# This file defines all profiling categories with their sub-categories. It is +# parsed by generate_profiling_categories.py at build time to create +# ProfilingCategoryList.h and profiling_categories.rs files. +# +# Profiling sub-categories are applied to each sampled stack to describe the +# type of workload that the CPU is busy with. Only one sub-category can be +# assigned so be mindful that these are non-overlapping. The active category is +# set by pushing a label to the profiling stack, or by the unwinder in cases +# such as JITs. A profile sample in arbitrary C++/Rust will typically be +# categorized based on the top of the label stack. +# +# Each category consists of a name and a set of attributes that are described below: +# +# name [required] +# ==== +# Name of the profiling category. This will be used in the C++ enum fields (not +# by Rust). +# +# label [required] +# ===== +# Label of the profiling category. This a more human readable string for the +# category. Label will be displayed in the Firefox Profiler front-end. But also +# this will be used as a Rust enum field (with non-alphanumeric characters +# removed) because it's more idiomatic for Rust enums than name fields (which +# are snake cased fields with all caps, which is not idiomatic to rust enum +# field). +# +# color [required] +# ===== +# Color that this category will show up as in the Firefox Profiler front-end. +# The list of available color names for categories is: +# - transparent +# - blue +# - green +# - grey +# - lightblue +# - magenta +# - orange +# - purple +# - yellow +# +# subcategories [required] +# ============= +# A list of sub-categories that belong to this category. +# There must be at least one sub-category for each category and there must be at +# least one category with the same name as the category to indicate the default +# sub-category. Each sub-category must have name and label attributes. +# +# name attribute should either be the same as the category (for default +# sub-category) or should start with parent category name + underscore +# (e.g. JS_Parsing). +# +# label attribute has the same purpose as parent category label attribute. +# +# For example: +# - name: JS +# subcategories: +# - name: JS +# label: Other +# - name: JS_Parsing +# label: Parsing +# +# Note that the first sub-category has the same name with the category. This is +# the default sub-category. Also note the other sub-categories starting with the +# category name + underscore. +# + +- name: IDLE + label: Idle + color: transparent + subcategories: + - name: IDLE + label: Other + +- name: OTHER + label: Other + color: grey + subcategories: + - name: OTHER + label: Other + - name: OTHER_PreferenceRead + label: Preference Read + - name: OTHER_Profiling + label: Profiling + +- name: TEST + label: Test + color: darkgray + subcategories: + - name: TEST + label: Test + +- name: LAYOUT + label: Layout + color: purple + subcategories: + - name: LAYOUT + label: Other + - name: LAYOUT_FrameConstruction + label: Frame construction + - name: LAYOUT_Reflow + label: Reflow + - name: LAYOUT_CSSParsing + label: CSS parsing + - name: LAYOUT_SelectorQuery + label: Selector query + - name: LAYOUT_StyleComputation + label: Style computation + - name: LAYOUT_Destroy + label: Layout cleanup + +- name: JS + label: JavaScript + color: yellow + subcategories: + - name: JS + label: Other + - name: JS_Parsing + label: Parsing + - name: JS_BaselineCompilation + label: JIT Compile (baseline) + - name: JS_IonCompilation + label: JIT Compile (ion) + - name: JS_Interpreter + label: Interpreter + - name: JS_BaselineInterpret + label: JIT (baseline-interpreter) + - name: JS_Baseline + label: JIT (baseline) + - name: JS_IonMonkey + label: JIT (ion) + - name: JS_Builtin + label: Builtin API + - name: JS_Wasm + label: Wasm + +- name: GCCC + label: GC / CC + color: orange + subcategories: + - name: GCCC + label: Other + - name: GCCC_MinorGC + label: Minor GC + - name: GCCC_MajorGC + label: Major GC (Other) + - name: GCCC_MajorGC_Mark + label: Major GC (Mark) + - name: GCCC_MajorGC_Sweep + label: Major GC (Sweep) + - name: GCCC_MajorGC_Compact + label: Major GC (Compact) + - name: GCCC_UnmarkGray + label: Unmark Gray + - name: GCCC_Barrier + label: Barrier + - name: GCCC_FreeSnowWhite + label: CC (Free Snow White) + - name: GCCC_BuildGraph + label: CC (Build Graph) + - name: GCCC_ScanRoots + label: CC (Scan Roots) + - name: GCCC_CollectWhite + label: CC (Collect White) + - name: GCCC_Finalize + label: CC (Finalize) + +- name: NETWORK + label: Network + color: lightblue + subcategories: + - name: NETWORK + label: Other + +- name: GRAPHICS + label: Graphics + color: green + subcategories: + - name: GRAPHICS + label: Other + - name: GRAPHICS_DisplayListBuilding + label: DisplayList building + - name: GRAPHICS_DisplayListMerging + label: DisplayList merging + - name: GRAPHICS_LayerBuilding + label: Layer building + - name: GRAPHICS_TileAllocation + label: Tile allocation + - name: GRAPHICS_WRDisplayList + label: WebRender display list + - name: GRAPHICS_Rasterization + label: Rasterization + - name: GRAPHICS_FlushingAsyncPaints + label: Flushing async paints + - name: GRAPHICS_ImageDecoding + label: Image decoding + +- name: DOM + label: DOM + color: blue + subcategories: + - name: DOM + label: Other + +- name: JAVA_ANDROID + label: Android + color: yellow + subcategories: + - name: JAVA_ANDROID + label: Other + +- name: JAVA_ANDROIDX + label: AndroidX + color: orange + subcategories: + - name: JAVA_ANDROIDX + label: Other + +- name: JAVA_LANGUAGE + label: Java + color: blue + subcategories: + - name: JAVA_LANGUAGE + label: Other + +- name: JAVA_MOZILLA + label: Mozilla + color: green + subcategories: + - name: JAVA_MOZILLA + label: Other + +- name: JAVA_KOTLIN + label: Kotlin + color: purple + subcategories: + - name: JAVA_KOTLIN + label: Other + +- name: JAVA_BLOCKED + label: Blocked + color: lightblue + subcategories: + - name: JAVA_BLOCKED + label: Other + +- name: IPC + label: IPC + color: lightgreen + subcategories: + - name: IPC + label: Other + +- name: MEDIA + label: Media + color: orange + subcategories: + - name: MEDIA + label: Other + - name: MEDIA_CUBEB + label: Cubeb + - name: MEDIA_PLAYBACK + label: Playback + - name: MEDIA_RT + label: Real-time rendering + +# We don't name this category ACCESSIBILITY +# because it's already defined as a macro. +- name: A11Y + label: Accessibility + color: brown + subcategories: + - name: A11Y + label: Other + +- name: PROFILER + label: Profiler + color: lightred + subcategories: + - name: PROFILER + label: Other + +- name: TIMER + label: Timer + color: grey + subcategories: + - name: TIMER + label: Other + +- name: REMOTE_PROTOCOL + label: Remote-Protocol + color: grey + subcategories: + - name: REMOTE_PROTOCOL + label: Other |