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