Zprofile.patch (18288B)
1 This reverts 2 https://github.com/rust-lang/rust/commit/659e20fa7524f8fd217476daf5ecbbe366b2ae61 3 and 4 https://github.com/rust-lang/rust/pull/131829 5 6 to restore support for -Zprofile (gcov-style coverage) that was removed from 7 rustc 1.84.0 8 9 diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs 10 index c548f467583..61bac4fbdab 100644 11 --- a/compiler/rustc_codegen_llvm/src/attributes.rs 12 +++ b/compiler/rustc_codegen_llvm/src/attributes.rs 13 @@ -250,6 +250,11 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { 14 return None; 15 } 16 17 + // probestack doesn't play nice either with gcov profiling. 18 + if cx.sess().opts.unstable_opts.profile { 19 + return None; 20 + } 21 + 22 let attr_value = match cx.sess().target.stack_probes { 23 StackProbeType::None => return None, 24 // Request LLVM to generate the probes inline. If the given LLVM version does not support 25 diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs 26 index 85a06f457eb..aa199a8fb26 100644 27 --- a/compiler/rustc_codegen_llvm/src/back/write.rs 28 +++ b/compiler/rustc_codegen_llvm/src/back/write.rs 29 @@ -693,6 +693,7 @@ pub(crate) unsafe fn llvm_optimize( 30 pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), 31 config.instrument_coverage, 32 instr_profile_output_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), 33 + config.instrument_gcov, 34 pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), 35 config.debug_info_for_profiling, 36 llvm_selfprofiler, 37 diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs 38 index 0e9dbfba658..af3e145f016 100644 39 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs 40 +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs 41 @@ -9,6 +9,7 @@ 42 use rustc_abi::{Align, Size}; 43 use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; 44 use rustc_codegen_ssa::traits::*; 45 +use rustc_fs_util::path_to_c_string; 46 use rustc_hir::def::{CtorKind, DefKind}; 47 use rustc_hir::def_id::{DefId, LOCAL_CRATE}; 48 use rustc_middle::bug; 49 @@ -968,8 +969,33 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( 50 debug_name_table_kind, 51 ); 52 53 + if tcx.sess.opts.unstable_opts.profile { 54 + let default_gcda_path = &output_filenames.with_extension("gcda"); 55 + let gcda_path = 56 + tcx.sess.opts.unstable_opts.profile_emit.as_ref().unwrap_or(default_gcda_path); 57 + 58 + let gcov_cu_info = [ 59 + path_to_mdstring(debug_context.llcontext, &output_filenames.with_extension("gcno")), 60 + path_to_mdstring(debug_context.llcontext, gcda_path), 61 + unit_metadata, 62 + ]; 63 + let gcov_metadata = llvm::LLVMMDNodeInContext2( 64 + debug_context.llcontext, 65 + gcov_cu_info.as_ptr(), 66 + gcov_cu_info.len(), 67 + ); 68 + let val = llvm::LLVMMetadataAsValue(debug_context.llcontext, gcov_metadata); 69 + 70 + llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, c"llvm.gcov".as_ptr(), val); 71 + } 72 + 73 return unit_metadata; 74 }; 75 + 76 + fn path_to_mdstring<'ll>(llcx: &'ll llvm::Context, path: &Path) -> &'ll llvm::Metadata { 77 + let path_str = path_to_c_string(path); 78 + unsafe { llvm::LLVMMDStringInContext2(llcx, path_str.as_ptr(), path_str.as_bytes().len()) } 79 + } 80 } 81 82 /// Creates a `DW_TAG_member` entry inside the DIE represented by the given `type_di_node`. 83 diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs 84 index 6cbf2dbf7d3..2aa628efae5 100644 85 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs 86 +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs 87 @@ -61,6 +61,7 @@ 88 89 /// A context object for maintaining all state needed by the debuginfo module. 90 pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { 91 + llcontext: &'ll llvm::Context, 92 llmod: &'ll llvm::Module, 93 builder: DIBuilderBox<'ll>, 94 created_files: RefCell<UnordMap<Option<(StableSourceFileId, SourceFileHash)>, &'ll DIFile>>, 95 @@ -76,7 +77,9 @@ pub(crate) fn new(llmod: &'ll llvm::Module) -> Self { 96 debug!("CodegenUnitDebugContext::new"); 97 let builder = DIBuilderBox::new(llmod); 98 // DIBuilder inherits context from the module, so we'd better use the same one 99 + let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; 100 CodegenUnitDebugContext { 101 + llcontext, 102 llmod, 103 builder, 104 created_files: Default::default(), 105 diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs 106 index 2443194ff48..e380af246a4 100644 107 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs 108 +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs 109 @@ -1019,6 +1019,7 @@ pub(crate) fn LLVMModuleCreateWithNameInContext( 110 ModuleID: *const c_char, 111 C: &Context, 112 ) -> &Module; 113 + pub(crate) fn LLVMGetModuleContext(M: &Module) -> &Context; 114 pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; 115 116 /// Data layout. See Module::getDataLayout. 117 @@ -2488,6 +2489,7 @@ pub(crate) fn LLVMRustOptimize<'a>( 118 PGOUsePath: *const c_char, 119 InstrumentCoverage: bool, 120 InstrProfileOutput: *const c_char, 121 + InstrumentGCOV: bool, 122 PGOSampleUsePath: *const c_char, 123 DebugInfoForProfiling: bool, 124 llvm_selfprofiler: *mut c_void, 125 diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs 126 index aa29afb7f5b..1fee2e45ebf 100644 127 --- a/compiler/rustc_codegen_ssa/src/back/write.rs 128 +++ b/compiler/rustc_codegen_ssa/src/back/write.rs 129 @@ -87,6 +87,7 @@ pub struct ModuleConfig { 130 pub pgo_sample_use: Option<PathBuf>, 131 pub debug_info_for_profiling: bool, 132 pub instrument_coverage: bool, 133 + pub instrument_gcov: bool, 134 135 pub sanitizer: SanitizerSet, 136 pub sanitizer_recover: SanitizerSet, 137 @@ -121,7 +122,12 @@ pub struct ModuleConfig { 138 } 139 140 impl ModuleConfig { 141 - fn new(kind: ModuleKind, tcx: TyCtxt<'_>, no_builtins: bool) -> ModuleConfig { 142 + fn new( 143 + kind: ModuleKind, 144 + tcx: TyCtxt<'_>, 145 + no_builtins: bool, 146 + is_compiler_builtins: bool, 147 + ) -> ModuleConfig { 148 // If it's a regular module, use `$regular`, otherwise use `$other`. 149 // `$regular` and `$other` are evaluated lazily. 150 macro_rules! if_regular { 151 @@ -181,6 +187,13 @@ macro_rules! if_regular { 152 pgo_sample_use: if_regular!(sess.opts.unstable_opts.profile_sample_use.clone(), None), 153 debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling, 154 instrument_coverage: if_regular!(sess.instrument_coverage(), false), 155 + instrument_gcov: if_regular!( 156 + // compiler_builtins overrides the codegen-units settings, 157 + // which is incompatible with -Zprofile which requires that 158 + // only a single codegen unit is used per crate. 159 + sess.opts.unstable_opts.profile && !is_compiler_builtins, 160 + false 161 + ), 162 163 sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()), 164 sanitizer_dataflow_abilist: if_regular!( 165 @@ -460,11 +473,14 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( 166 167 let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); 168 let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); 169 + let is_compiler_builtins = attr::contains_name(crate_attrs, sym::compiler_builtins); 170 171 let crate_info = CrateInfo::new(tcx, target_cpu); 172 173 - let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); 174 - let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); 175 + let regular_config = 176 + ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins, is_compiler_builtins); 177 + let allocator_config = 178 + ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins, is_compiler_builtins); 179 180 let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); 181 let (codegen_worker_send, codegen_worker_receive) = channel(); 182 diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs 183 index 8771bb44050..f82a4d6ce19 100644 184 --- a/compiler/rustc_interface/src/tests.rs 185 +++ b/compiler/rustc_interface/src/tests.rs 186 @@ -848,6 +848,8 @@ macro_rules! tracked { 187 tracked!(plt, Some(true)); 188 tracked!(polonius, Polonius::Legacy); 189 tracked!(precise_enum_drop_elaboration, false); 190 + tracked!(profile, true); 191 + tracked!(profile_emit, Some(PathBuf::from("abc"))); 192 tracked!(profile_sample_use, Some(PathBuf::from("abc"))); 193 tracked!(profiler_runtime, "abc".to_string()); 194 tracked!(reg_struct_return, true); 195 diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp 196 index 8c34052770e..d807abfa9b5 100644 197 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp 198 +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp 199 @@ -35,6 +35,7 @@ 200 #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" 201 #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" 202 #include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h" 203 +#include "llvm/Transforms/Instrumentation/GCOVProfiler.h" 204 #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" 205 #include "llvm/Transforms/Instrumentation/InstrProfiling.h" 206 #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" 207 @@ -720,8 +721,9 @@ extern "C" LLVMRustResult LLVMRustOptimize( 208 bool PrintAfterEnzyme, bool PrintPasses, 209 LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, 210 const char *PGOUsePath, bool InstrumentCoverage, 211 - const char *InstrProfileOutput, const char *PGOSampleUsePath, 212 - bool DebugInfoForProfiling, void *LlvmSelfProfiler, 213 + const char *InstrProfileOutput, bool InstrumentGCOV, 214 + const char *PGOSampleUsePath, bool DebugInfoForProfiling, 215 + void *LlvmSelfProfiler, 216 LLVMRustSelfProfileBeforePassCallback BeforePassCallback, 217 LLVMRustSelfProfileAfterPassCallback AfterPassCallback, 218 const char *ExtraPasses, size_t ExtraPassesLen, const char *LLVMPlugins, 219 @@ -850,6 +852,13 @@ extern "C" LLVMRustResult LLVMRustOptimize( 220 }); 221 } 222 223 + if (InstrumentGCOV) { 224 + PipelineStartEPCallbacks.push_back( 225 + [](ModulePassManager &MPM, OptimizationLevel Level) { 226 + MPM.addPass(GCOVProfilerPass(GCOVOptions::getDefault())); 227 + }); 228 + } 229 + 230 if (InstrumentCoverage) { 231 PipelineStartEPCallbacks.push_back( 232 [InstrProfileOutput](ModulePassManager &MPM, OptimizationLevel Level) { 233 diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs 234 index 6bfb3769f24..14700bea72b 100644 235 --- a/compiler/rustc_metadata/src/creader.rs 236 +++ b/compiler/rustc_metadata/src/creader.rs 237 @@ -1039,7 +1039,7 @@ fn inject_panic_runtime(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) { 238 239 fn inject_profiler_runtime(&mut self, tcx: TyCtxt<'_>) { 240 let needs_profiler_runtime = 241 - tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled(); 242 + tcx.sess.instrument_coverage() || tcx.sess.opts.unstable_opts.profile || tcx.sess.opts.cg.profile_generate.enabled(); 243 if !needs_profiler_runtime || tcx.sess.opts.unstable_opts.no_profiler_runtime { 244 return; 245 } 246 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs 247 index 8f624e0fb2f..9d6e376c16f 100644 248 --- a/compiler/rustc_session/src/config.rs 249 +++ b/compiler/rustc_session/src/config.rs 250 @@ -2606,7 +2606,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M 251 let output_types = parse_output_types(early_dcx, &unstable_opts, matches); 252 253 let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); 254 - let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( 255 + let (disable_local_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( 256 early_dcx, 257 &output_types, 258 matches, 259 @@ -2625,6 +2625,18 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M 260 261 let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state); 262 263 + if unstable_opts.profile && incremental.is_some() { 264 + early_dcx.early_fatal("can't instrument with gcov profiling when compiling incrementally"); 265 + } 266 + if unstable_opts.profile { 267 + match codegen_units { 268 + Some(1) => {} 269 + None => codegen_units = Some(1), 270 + Some(_) => early_dcx 271 + .early_fatal("can't instrument with gcov profiling with multiple codegen units"), 272 + } 273 + } 274 + 275 if cg.profile_generate.enabled() && cg.profile_use.is_some() { 276 early_dcx.early_fatal("options `-C profile-generate` and `-C profile-use` are exclusive"); 277 } 278 diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs 279 index 44b35e8921e..235e04fa5b0 100644 280 --- a/compiler/rustc_session/src/options.rs 281 +++ b/compiler/rustc_session/src/options.rs 282 @@ -2471,8 +2471,13 @@ pub(crate) fn parse_align(slot: &mut Option<Align>, v: Option<&str>) -> bool { 283 proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread, 284 parse_proc_macro_execution_strategy, [UNTRACKED], 285 "how to run proc-macro code (default: same-thread)"), 286 + profile: bool = (false, parse_bool, [TRACKED], 287 + "insert profiling code (default: no)"), 288 profile_closures: bool = (false, parse_no_value, [UNTRACKED], 289 "profile size of closures"), 290 + profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], 291 + "file path to emit profiling data at runtime when using 'profile' \ 292 + (default based on relative source path)"), 293 profile_sample_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], 294 "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), 295 profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], 296 diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md 297 index 57679f82f48..0f88bec2c71 100644 298 --- a/src/doc/rustc/src/instrument-coverage.md 299 +++ b/src/doc/rustc/src/instrument-coverage.md 300 @@ -2,8 +2,12 @@ 301 302 ## Introduction 303 304 -This document describes how to enable and use LLVM instrumentation-based coverage, 305 -via the `-C instrument-coverage` compiler flag. 306 +The Rust compiler includes two code coverage implementations: 307 + 308 +- A GCC-compatible, gcov-based coverage implementation, enabled with `-Z profile`, which derives coverage data based on DebugInfo. 309 +- A source-based code coverage implementation, enabled with `-C instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data. 310 + 311 +This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-C instrument-coverage` compiler flag. 312 313 ## How it works 314 315 diff --git a/src/doc/unstable-book/src/compiler-flags/profile.md b/src/doc/unstable-book/src/compiler-flags/profile.md 316 new file mode 100644 317 index 00000000000..71303bfaff2 318 --- /dev/null 319 +++ b/src/doc/unstable-book/src/compiler-flags/profile.md 320 @@ -0,0 +1,27 @@ 321 +# `profile` 322 + 323 +The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524). 324 + 325 +------------------------ 326 + 327 +This feature allows the generation of code coverage reports. 328 + 329 +Set the `-Zprofile` compiler flag in order to enable gcov profiling. 330 + 331 +For example: 332 +```Bash 333 +cargo new testgcov --bin 334 +cd testgcov 335 +export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" 336 +export CARGO_INCREMENTAL=0 337 +cargo build 338 +cargo run 339 +``` 340 + 341 +Once you've built and run your program, files with the `gcno` (after build) and `gcda` (after execution) extensions will be created. 342 +You can parse them with [llvm-cov gcov](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-gcov) or [grcov](https://github.com/mozilla/grcov). 343 + 344 +Please note that `RUSTFLAGS` by default applies to everything that cargo builds and runs during a build! 345 +When the `--target` flag is explicitly passed to cargo, the `RUSTFLAGS` no longer apply to build scripts and procedural macros. 346 +For more fine-grained control consider passing a `RUSTC_WRAPPER` program to cargo that only adds the profiling flags to 347 +rustc for the specific crates you want to profile. 348 diff --git a/tests/run-make/profile/rmake.rs b/tests/run-make/profile/rmake.rs 349 new file mode 100644 350 index 00000000000..58a1b53c040 351 --- /dev/null 352 +++ b/tests/run-make/profile/rmake.rs 353 @@ -0,0 +1,21 @@ 354 +// This test revolves around the rustc flag -Z profile, which should 355 +// generate a .gcno file (initial profiling information) as well 356 +// as a .gcda file (branch counters). The path where these are emitted 357 +// should also be configurable with -Z profile-emit. This test checks 358 +// that the files are produced, and then that the latter flag is respected. 359 +// See https://github.com/rust-lang/rust/pull/42433 360 + 361 +//@ ignore-cross-compile 362 +//@ needs-profiler-runtime 363 + 364 +use run_make_support::{path, run, rustc}; 365 + 366 +fn main() { 367 + rustc().arg("-g").arg("-Zprofile").input("test.rs").run(); 368 + run("test"); 369 + assert!(path("test.gcno").exists(), "no .gcno file"); 370 + assert!(path("test.gcda").exists(), "no .gcda file"); 371 + rustc().arg("-g").arg("-Zprofile").arg("-Zprofile-emit=abc/abc.gcda").input("test.rs").run(); 372 + run("test"); 373 + assert!(path("abc/abc.gcda").exists(), "gcda file not emitted to defined path"); 374 +} 375 diff --git a/tests/run-make/profile/test.rs b/tests/run-make/profile/test.rs 376 new file mode 100644 377 index 00000000000..f328e4d9d04 378 --- /dev/null 379 +++ b/tests/run-make/profile/test.rs 380 @@ -0,0 +1 @@ 381 +fn main() {}