|
53 | 53 | "is_exec_configuration", |
54 | 54 | "is_std_dylib", |
55 | 55 | "make_static_lib_symlink", |
| 56 | + "matches_prefix_filter", |
56 | 57 | "parse_env_strings", |
57 | 58 | "relativize", |
58 | 59 | ) |
@@ -105,6 +106,19 @@ PerCrateRustcFlagsInfo = provider( |
105 | 106 | fields = {"per_crate_rustc_flags": "List[string] Extra flags to pass to rustc in non-exec configuration"}, |
106 | 107 | ) |
107 | 108 |
|
| 109 | +UnstableSelfProfileInfo = provider( |
| 110 | + doc = "Passes -Zself-profile and -Zself-profile-events flags to matching rust crates.", |
| 111 | + fields = { |
| 112 | + "events": ( |
| 113 | + "List[tuple[str, str]]: A list of `(pattern, event_types)` pairs. The `pattern` " + |
| 114 | + "matches against a target's label (with leading `@//` stripped) or " + |
| 115 | + "its execution path (an empty `pattern` matches all targets). The `event_types` " + |
| 116 | + "specifies comma-separated categories of self-profile events to pass to " + |
| 117 | + "`-Zself-profile-events` (e.g., `all`)." |
| 118 | + ), |
| 119 | + }, |
| 120 | +) |
| 121 | + |
108 | 122 | def _get_rustc_env(attr, toolchain, crate_name): |
109 | 123 | """Gathers rustc environment variables |
110 | 124 |
|
@@ -1455,6 +1469,45 @@ def collect_extra_rustc_flags(ctx, toolchain, crate_root, crate_type): |
1455 | 1469 |
|
1456 | 1470 | return flags |
1457 | 1471 |
|
| 1472 | +def setup_zself_profile(ctx, crate_info): |
| 1473 | + """Sets up rustc self-profiling if enabled by zself_profile_events. |
| 1474 | +
|
| 1475 | + Args: |
| 1476 | + ctx (ctx): The current rule's context object. |
| 1477 | + crate_info (CrateInfo): The CrateInfo provider of the target crate. |
| 1478 | +
|
| 1479 | + Returns: |
| 1480 | + tuple: A tuple containing: |
| 1481 | + - File: The declared self-profile directory, or None if disabled. |
| 1482 | + - list[str]: The self-profile flags to pass to rustc. |
| 1483 | + """ |
| 1484 | + if not getattr(ctx.attr, "zself_profile_events", None) or UnstableSelfProfileInfo not in ctx.attr.zself_profile_events: |
| 1485 | + return None, [] |
| 1486 | + |
| 1487 | + events_info = ctx.attr.zself_profile_events[UnstableSelfProfileInfo].events |
| 1488 | + |
| 1489 | + is_self_profile_enabled = False |
| 1490 | + event_types_to_use = None |
| 1491 | + |
| 1492 | + # Check if the current crate matches any of the specified prefix filters. |
| 1493 | + # Matching works by comparing against the target's label or its execution path. |
| 1494 | + for pattern, event_types in events_info: |
| 1495 | + if matches_prefix_filter(ctx.label, crate_info.root.path, pattern): |
| 1496 | + is_self_profile_enabled = True |
| 1497 | + event_types_to_use = event_types |
| 1498 | + break |
| 1499 | + |
| 1500 | + if not is_self_profile_enabled: |
| 1501 | + return None, [] |
| 1502 | + |
| 1503 | + profiling_dir = ctx.actions.declare_directory(crate_info.output.basename + "_self-profile", sibling = crate_info.output) |
| 1504 | + |
| 1505 | + profiling_flags = ["-Zself-profile=%s" % profiling_dir.path] |
| 1506 | + if event_types_to_use: |
| 1507 | + profiling_flags.append("-Zself-profile-events=%s" % event_types_to_use) |
| 1508 | + |
| 1509 | + return profiling_dir, profiling_flags |
| 1510 | + |
1458 | 1511 | def rustc_compile_action( |
1459 | 1512 | *, |
1460 | 1513 | ctx, |
@@ -1542,6 +1595,9 @@ def rustc_compile_action( |
1542 | 1595 | rust_flags = rust_flags + ctx.attr.lint_config[LintsInfo].rustc_lint_flags |
1543 | 1596 | lint_files = lint_files + ctx.attr.lint_config[LintsInfo].rustc_lint_files |
1544 | 1597 |
|
| 1598 | + profiling_dir, profiling_flags = setup_zself_profile(ctx, crate_info) |
| 1599 | + rust_flags = rust_flags + profiling_flags |
| 1600 | + |
1545 | 1601 | compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs( |
1546 | 1602 | ctx = ctx, |
1547 | 1603 | file = ctx.file, |
@@ -1676,6 +1732,8 @@ def rustc_compile_action( |
1676 | 1732 | action_outputs = list(outputs) |
1677 | 1733 | if rustc_output: |
1678 | 1734 | action_outputs.append(rustc_output) |
| 1735 | + if profiling_dir: |
| 1736 | + action_outputs.append(profiling_dir) |
1679 | 1737 |
|
1680 | 1738 | # Get the compilation mode for the current target. |
1681 | 1739 | compilation_mode = get_compilation_mode_opts(ctx, toolchain) |
@@ -1950,7 +2008,8 @@ def rustc_compile_action( |
1950 | 2008 | output_group_info["rustc_rmeta_output"] = depset([rustc_rmeta_output]) |
1951 | 2009 | if rustc_output: |
1952 | 2010 | output_group_info["rustc_output"] = depset([rustc_output]) |
1953 | | - |
| 2011 | + if profiling_dir: |
| 2012 | + output_group_info["self_profile"] = depset([profiling_dir]) |
1954 | 2013 | if output_group_info: |
1955 | 2014 | providers.append(OutputGroupInfo(**output_group_info)) |
1956 | 2015 |
|
@@ -2737,19 +2796,7 @@ def _collect_per_crate_rustc_flags(ctx, crate_root, per_crate_rustc_flags): |
2737 | 2796 | if not flag: |
2738 | 2797 | fail("per_crate_rustc_flag '{}' does not follow the expected format: prefix_filter@flag".format(per_crate_rustc_flag)) |
2739 | 2798 |
|
2740 | | - label_string = str(ctx.label) |
2741 | | - if label_string.startswith("@//"): |
2742 | | - label = label_string[1:] |
2743 | | - elif label_string.startswith( |
2744 | | - # buildifier: disable=canonical-repository |
2745 | | - "@@//", |
2746 | | - ): |
2747 | | - label = label_string[2:] |
2748 | | - else: |
2749 | | - label = label_string |
2750 | | - execution_path = crate_root.path |
2751 | | - |
2752 | | - if label.startswith(prefix_filter) or execution_path.startswith(prefix_filter): |
| 2799 | + if matches_prefix_filter(ctx.label, crate_root.path, prefix_filter): |
2753 | 2800 | flags.append(flag) |
2754 | 2801 |
|
2755 | 2802 | return flags |
@@ -2946,3 +2993,38 @@ no_std = rule( |
2946 | 2993 | }, |
2947 | 2994 | implementation = _no_std_impl, |
2948 | 2995 | ) |
| 2996 | + |
| 2997 | +def _zself_profile_events_impl(ctx): |
| 2998 | + events = [] |
| 2999 | + for val in ctx.build_setting_value: |
| 3000 | + if not val: |
| 3001 | + continue |
| 3002 | + if "@" in val: |
| 3003 | + pattern, event_types = val.split("@", 1) |
| 3004 | + events.append((pattern, event_types)) |
| 3005 | + else: |
| 3006 | + fail("zself_profile_events '{}' does not follow the expected format: prefix_filter@comma_separated_flag".format(val)) |
| 3007 | + return [UnstableSelfProfileInfo(events = events)] |
| 3008 | + |
| 3009 | +zself_profile_events = rule( |
| 3010 | + doc = ( |
| 3011 | + "Passes -Zself-profile and -Zself-profile-events flags to matching Rust crates." + |
| 3012 | + "This feature allows end-users to profile rustc compiler performance on specific crates " + |
| 3013 | + "using rustc's self-profiler. Because these flags are unstable, using them requires a " + |
| 3014 | + "nightly compiler toolchain. The setting is configured from the command line via " + |
| 3015 | + "`--@rules_rust//rust/settings:zself_profile_events`." + |
| 3016 | + "The expected value format is `<prefix_filter>@<events_specification>`. Multiple uses of " + |
| 3017 | + "this flag are accumulated, however only first <events_specification> will be applied for same" + |
| 3018 | + "<prefix_filter>." + |
| 3019 | + "If the target prefix matches with <prefix_filter>, `-Zself-profile` and `-Zself-profile-events` " + |
| 3020 | + "with values as `<crate_name>.self-profile/` and <events_specification> respectively " + |
| 3021 | + "is passed to rustc compiler. The generated profile files (e.g., `.mm_profdata`) are placed" + |
| 3022 | + "under `bazel-out/bin/path/to/package/crate_name_self-profile/` which can be seen by passing" + |
| 3023 | + " `--output_groups=self_profile` flag." + |
| 3024 | + "blaze build //my/project:my_lib \\" + |
| 3025 | + "--@rules_rust//rust/settings:zself_profile_events=//my/project@all \\" + |
| 3026 | + "--output_groups=self_profile" |
| 3027 | + ), |
| 3028 | + implementation = _zself_profile_events_impl, |
| 3029 | + build_setting = config.string_list(flag = True, repeatable = True), |
| 3030 | +) |
0 commit comments