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