Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cc/common/cc_helper.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ def _build_output_groups_for_emitting_compile_providers(
else:
output_groups_builder["module_files"] = depset(compilation_outputs._module_files)

# Add trace JSON files to output groups
if hasattr(compilation_outputs, "_trace_files") and compilation_outputs._trace_files:
output_groups_builder["trace_files"] = depset(compilation_outputs._trace_files)
if hasattr(compilation_outputs, "_pic_trace_files") and compilation_outputs._pic_trace_files:
pic_trace_group = output_groups_builder.get("trace_files", depset())
output_groups_builder["trace_files"] = depset(transitive = [pic_trace_group, depset(compilation_outputs._pic_trace_files)])

if generate_hidden_top_level_group:
output_groups_builder["_hidden_top_level_INTERNAL_"] = _collect_library_hidden_top_level_artifacts(
ctx,
Expand Down
14 changes: 14 additions & 0 deletions cc/private/compile/cc_compilation_outputs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ CcCompilationOutputsInfo = provider(
"_pic_dwo_files": "(list[File]) All .pic.dwo files built by the target, corresponding to .pic.o outputs.",
"_gcno_files": "(list[File]) All .gcno files built by the target, corresponding to .o outputs.",
"_pic_gcno_files": "(list[File]) All .pic.gcno files built by the target, corresponding to .pic.gcno outputs.",
"_trace_files": "(list[File]) All .json files built by the target, corresponding to .o outputs.",
"_pic_trace_files": "(list[File]) All .pic.json files built by the target, corresponding to .pic.o outputs.",
"temps": '(() -> depset[File]) All artifacts that are created if "--save_temps" is true.',
"_header_tokens": "(list[File]) All token .h.processed files created when preprocessing or parsing headers.",
"_module_files": "(list[File]) All .pcm files built by the target.",
Expand All @@ -52,6 +54,8 @@ def create_compilation_outputs_internal(
pic_dwo_files = [],
gcno_files = [],
pic_gcno_files = [],
trace_files = [],
pic_trace_files = [],
temps = [],
header_tokens = [],
module_files = [],
Expand All @@ -69,6 +73,8 @@ def create_compilation_outputs_internal(
pic_dwo_files: A list of PIC dwo files.
gcno_files: A list of gcno files.
pic_gcno_files: A list of PIC gcno files.
trace_files: A list of trace JSON files.
pic_trace_files: A list of PIC trace JSON files.
temps: A depset of temporary files.
header_tokens: A list of header tokens.
module_files: A list of module files.
Expand All @@ -91,6 +97,8 @@ def create_compilation_outputs_internal(
_lto_compilation_context = lto_compilation_context,
_gcno_files = _cc_internal.freeze(gcno_files),
_pic_gcno_files = _cc_internal.freeze(pic_gcno_files),
_trace_files = _cc_internal.freeze(trace_files),
_pic_trace_files = _cc_internal.freeze(pic_trace_files),
_dwo_files = _cc_internal.freeze(dwo_files),
_pic_dwo_files = _cc_internal.freeze(pic_dwo_files),
cpp_module_files = _cc_internal.freeze(cpp_module_files),
Expand Down Expand Up @@ -194,6 +202,8 @@ def merge_compilation_outputs(*, compilation_outputs):
pic_dwo_files = []
gcno_files = []
pic_gcno_files = []
trace_files = []
pic_trace_files = []
lto_compilation_contexts = []
transitive_temps = []
header_tokens = []
Expand All @@ -206,6 +216,8 @@ def merge_compilation_outputs(*, compilation_outputs):
pic_dwo_files.extend(co._pic_dwo_files)
gcno_files.extend(co._gcno_files)
pic_gcno_files.extend(co._pic_gcno_files)
trace_files.extend(co._trace_files)
pic_trace_files.extend(co._pic_trace_files)
transitive_temps.append(co.temps())
header_tokens.extend(co._header_tokens)
module_files.extend(co._module_files)
Expand All @@ -220,6 +232,8 @@ def merge_compilation_outputs(*, compilation_outputs):
_pic_dwo_files = pic_dwo_files,
_gcno_files = gcno_files,
_pic_gcno_files = pic_gcno_files,
_trace_files = trace_files,
_pic_trace_files = pic_trace_files,
temps = wrap_with_check_private_api(depset(transitive = transitive_temps)),
_header_tokens = header_tokens,
_module_files = module_files,
Expand Down
45 changes: 44 additions & 1 deletion cc/private/compile/compile.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ load("//cc/private/compile:lto_compilation_context.bzl", "create_lto_compilation

_VALID_CPP_SOURCE_TYPES = set([CPP_SOURCE_TYPE_SOURCE, CPP_SOURCE_TYPE_HEADER, CPP_SOURCE_TYPE_CLIF_INPUT_PROTO])

def _clang_trace_enabled(feature_configuration):
return feature_configuration.is_enabled("clang_trace")

def _cpp_source_init(*, label, source, type):
if type not in _VALID_CPP_SOURCE_TYPES:
fail("invalid type of cpp source, got:", type, "expected one of:", _VALID_CPP_SOURCE_TYPES)
Expand Down Expand Up @@ -347,6 +350,8 @@ def compile(
"lto_compilation_context": {},
"gcno_files": [],
"pic_gcno_files": [],
"trace_files": [],
"pic_trace_files": [],
"dwo_files": [],
"pic_dwo_files": [],
"cpp_module_files": [],
Expand Down Expand Up @@ -837,6 +842,7 @@ def _create_cc_compile_actions_with_cpp20_module_helper(
cpp_module_map = cc_compilation_context._module_map,
add_object = True,
enable_coverage = is_code_coverage_enabled,
enable_trace = _clang_trace_enabled(feature_configuration),
generate_dwo = should_create_per_object_debug_info(feature_configuration, cpp_configuration),
bitcode_output = bitcode_output,
fdo_context = fdo_context,
Expand Down Expand Up @@ -985,6 +991,7 @@ def _create_cc_compile_actions_with_cpp20_module_helper(
cpp_module_map = cc_compilation_context._module_map,
add_object = True,
enable_coverage = is_code_coverage_enabled,
enable_trace = _clang_trace_enabled(feature_configuration),
generate_dwo = should_create_per_object_debug_info(feature_configuration, cpp_configuration),
bitcode_output = bitcode_output,
fdo_context = fdo_context,
Expand Down Expand Up @@ -1268,6 +1275,7 @@ def _create_cc_compile_actions(
cpp_module_map = cc_compilation_context._module_map,
add_object = True,
enable_coverage = is_code_coverage_enabled,
enable_trace = _clang_trace_enabled(feature_configuration),
generate_dwo = should_create_per_object_debug_info(feature_configuration, cpp_configuration),
bitcode_output = bitcode_output,
fdo_context = fdo_context,
Expand Down Expand Up @@ -1420,6 +1428,7 @@ def _create_pic_nopic_compile_source_actions(
cpp_module_map,
add_object,
enable_coverage,
enable_trace,
generate_dwo,
bitcode_output,
fdo_context,
Expand Down Expand Up @@ -1454,6 +1463,7 @@ def _create_pic_nopic_compile_source_actions(
cpp_module_map = cpp_module_map,
add_object = add_object,
enable_coverage = enable_coverage,
enable_trace = enable_trace,
generate_dwo = generate_dwo,
bitcode_output = bitcode_output,
fdo_context = fdo_context,
Expand Down Expand Up @@ -1491,6 +1501,7 @@ def _create_pic_nopic_compile_source_actions(
cpp_module_map = cpp_module_map,
add_object = add_object,
enable_coverage = enable_coverage,
enable_trace = enable_trace,
generate_dwo = generate_dwo,
bitcode_output = bitcode_output,
fdo_context = fdo_context,
Expand Down Expand Up @@ -1529,6 +1540,7 @@ def _create_compile_source_action(
cpp_module_map,
add_object,
enable_coverage,
enable_trace,
generate_dwo,
bitcode_output,
fdo_context,
Expand Down Expand Up @@ -1592,6 +1604,11 @@ def _create_compile_source_action(
configuration = configuration,
enable_coverage = enable_coverage,
)
trace_file = _maybe_declare_trace_file(
ctx = action_construction_context,
enable_trace = enable_trace,
object_file = object_file,
)

dwo_file = None
if generate_dwo and not bitcode_output:
Expand Down Expand Up @@ -1680,14 +1697,20 @@ def _create_compile_source_action(
if add_object and fdo_context_has_artifacts:
additional_inputs = additional_compilation_inputs + auxiliary_fdo_inputs.to_list()

all_additional_outputs = list(additional_outputs)
if trace_file:
all_additional_outputs.append(trace_file)

# Provide these args conditionally as they require a recent version of Bazel.
if modmap_file:
module_args = {
"additional_outputs": additional_outputs,
"additional_outputs": all_additional_outputs,
"module_files": module_files,
"modmap_file": modmap_file,
"modmap_input_file": modmap_input_file,
}
elif all_additional_outputs:
module_args = {"additional_outputs": all_additional_outputs}
else:
module_args = {}

Expand Down Expand Up @@ -1736,6 +1759,11 @@ def _create_compile_source_action(
outputs["pic_gcno_files"].append(gcno_file)
else:
outputs["gcno_files"].append(gcno_file)
if trace_file:
if use_pic:
outputs["pic_trace_files"].append(trace_file)
else:
outputs["trace_files"].append(trace_file)
return object_file

def _create_temps_action(
Expand Down Expand Up @@ -2136,6 +2164,7 @@ def _create_module_action(
cpp_module_map = cpp_module_map,
add_object = False,
enable_coverage = False,
enable_trace = False,
generate_dwo = False,
bitcode_output = False,
additional_compilation_inputs = additional_compilation_inputs,
Expand Down Expand Up @@ -2297,12 +2326,25 @@ def _maybe_declare_gcno_file(
)
return gcno_file

def _maybe_declare_trace_file(
ctx,
enable_trace,
object_file):
if not enable_trace:
return None
return _cc_internal.declare_other_output_file(
ctx = ctx,
output_name = paths.replace_extension(paths.basename(object_file.path), ".json"),
object_file = object_file,
)

def _create_compile_action(
*,
action_construction_context = None,
action_name = None,
additional_compilation_inputs = None,
additional_include_scanning_roots = [],
additional_outputs = [],
cc_compilation_context = None,
cc_toolchain = None,
compile_build_variables = None,
Expand All @@ -2329,6 +2371,7 @@ def _create_compile_action(
action_name = action_name,
additional_compilation_inputs = additional_compilation_inputs,
additional_include_scanning_roots = additional_include_scanning_roots,
additional_outputs = additional_outputs,
cc_compilation_context = cc_compilation_context,
cc_toolchain = cc_toolchain,
compile_build_variables = compile_build_variables,
Expand Down
1 change: 0 additions & 1 deletion cc/private/compile/compile_build_variables.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,6 @@ def get_specific_compile_build_variables(
# TODO: Blaze currently uses `gcov_gcno_file` to detect if code coverage
# is enabled. It should use a different signal.
result[_VARS.GCOV_GCNO_FILE] = ""

if dwo_file:
result[_VARS.PER_OBJECT_DEBUG_INFO_FILE] = dwo_file
if using_fission:
Expand Down
34 changes: 34 additions & 0 deletions cc/private/toolchain/unix_cc_toolchain_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,35 @@ def _impl(ctx):

skip_virtual_includes_feature = feature(name = "skip_virtual_includes")

# Clang -ftime-trace support
trace_feature = feature(
name = "trace",
provides = ["trace"],
)

clang_trace_feature = feature(
name = "clang_trace",
flag_sets = [
flag_set(
actions = [
ACTION_NAMES.preprocess_assemble,
ACTION_NAMES.c_compile,
ACTION_NAMES.cpp_compile,
ACTION_NAMES.cpp_module_compile,
ACTION_NAMES.objc_compile,
ACTION_NAMES.objcpp_compile,
],
flag_groups = [
flag_group(
flags = ["-ftime-trace"],
),
],
),
],
requires = [feature_set(features = ["trace"])],
enabled = True,
)

# TODO(#8303): Mac crosstool should also declare every feature.
if is_linux:
# Linux artifact name patterns are the default.
Expand Down Expand Up @@ -2016,6 +2045,11 @@ def _impl(ctx):
extra_rules_based_features = depset(ctx.attr.extra_enabled_features + ctx.attr.extra_known_features)
features.extend([convert_feature(extra_feature[FeatureInfo], enabled = extra_feature in ctx.attr.extra_enabled_features) for extra_feature in extra_rules_based_features.to_list()])

features.extend([
trace_feature,
])
if ctx.attr.compiler == "clang":
features.append(clang_trace_feature)
return cc_common.create_cc_toolchain_config_info(
ctx = ctx,
features = features,
Expand Down
11 changes: 11 additions & 0 deletions tests/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,14 @@ rules_cc_integration_test(
target_compatible_with = VERSION_GATE,
test_script = "cc_static_library_failure_tests.sh",
)

rules_cc_integration_test(
name = "ftime_trace_tests",
timeout = "long",
target_compatible_with = select({
"@platforms//os:macos": ["@platforms//:incompatible"],
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
test_script = "ftime_trace_tests.sh",
)
76 changes: 76 additions & 0 deletions tests/integration/ftime_trace_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
set -euo pipefail

source "$(rlocation rules_cc/tests/test_utils.sh)"
source "$(rlocation rules_cc/tests/unittest.bash)"

function set_up() {
if is_windows; then
echo "Skipping on Windows."
return 1
fi

local -r clang="$(which clang || true)"
local -r clangxx="$(which clang++ || true)"
if [[ ! -x "$clang" || ! -x "$clangxx" ]]; then
echo "clang/clang++ not installed. Skipping test."
return 1
fi

add_to_bazelrc "common --repo_env=CC=$clang"
add_to_bazelrc "common --repo_env=CXX=$clangxx"
}

function test_ftime_trace_outputs() {
# Bazel versions before 9 use the built-in native C++ rules, not rules_cc.
# This integration test exercises the rules_cc -ftime-trace implementation only.
local bazel_version=""
bazel_version="$(bazel version 2>/dev/null | awk '/^(Build label|Release label):/ { print $3; exit }')"
local bazel_major="${bazel_version%%.*}"
if [[ -z "$bazel_major" || "$bazel_major" -lt 9 ]]; then
echo "Bazel ${bazel_version:-unknown} is older than 9.0; native C++ rules are used instead of rules_cc. Skipping test."
return 0
fi

cat > BUILD << EOF
load("@rules_cc//cc:cc_library.bzl", "cc_library")

cc_library(
name = "lib_with_trace",
srcs = ["foo.cc"],
features = ["trace"],
)
EOF

cat > foo.cc << EOF
int foo() {
return 42;
}
EOF

bazel build //:lib_with_trace --output_groups=trace_files >& "$TEST_log" || fail "Build failed"

trace_json="$(find bazel-bin/_objs/lib_with_trace -name '*.json' | head -1)"
if [[ -z "$trace_json" ]]; then
fail "Expected a Clang -ftime-trace JSON file under bazel-bin/_objs/lib_with_trace"
fi

if [[ ! -s "$trace_json" ]]; then
fail "Trace file is empty: $trace_json"
fi

python3 - "$trace_json" << 'PY'
import json
import sys

path = sys.argv[1]
with open(path, encoding="utf-8") as input_file:
data = json.load(input_file)

if "traceEvents" not in data:
raise SystemExit(f"missing traceEvents key in {path}")
if not isinstance(data["traceEvents"], list) or not data["traceEvents"]:
raise SystemExit(f"traceEvents is empty in {path}")
PY
}

run_suite "Integration tests for Clang -ftime-trace with rules_cc"