Skip to content

Commit f347c42

Browse files
authored
Rollup merge of #100384 - ridwanabdillahi:instr_profile_output, r=wesleywiser
Add support for generating unique profraw files by default when using `-C instrument-coverage` Currently, enabling the rustc flag `-C instrument-coverage` instruments the given crate and by default uses the naming scheme `default.profraw` for any instrumented profile files generated during the execution of a binary linked against this crate. This leads to multiple binaries being executed overwriting one another and causing only the last executable run to contain actual coverage results. This can be overridden by manually setting the environment variable `LLVM_PROFILE_FILE` to use a unique naming scheme. This PR adds a change to add support for a reasonable default for rustc to use when enabling coverage instrumentation similar to how the Rust compiler treats generating these same `profraw` files when PGO is enabled. The new naming scheme is set to `default_%m_%p.profraw` to ensure the uniqueness of each file being generated using [LLVMs special pattern strings](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program). Today the compiler sets the default for PGO `profraw` files to `default_%m.profraw` to ensure a unique file for each run. The same can be done for the instrumented profile files generated via the `-C instrument-coverage` flag as well which LLVM has API support for. Linked Issue: #100381 r? `@wesleywiser`
2 parents 836f706 + 7e49c1b commit f347c42

File tree

5 files changed

+54
-10
lines changed

5 files changed

+54
-10
lines changed

compiler/rustc_codegen_llvm/src/back/write.rs

+10
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,14 @@ fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option<CString> {
423423
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap())
424424
}
425425

426+
fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> {
427+
if config.instrument_coverage {
428+
Some(CString::new("default_%m_%p.profraw").unwrap())
429+
} else {
430+
None
431+
}
432+
}
433+
426434
pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
427435
cgcx: &CodegenContext<LlvmCodegenBackend>,
428436
diag_handler: &Handler,
@@ -438,6 +446,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
438446
let pgo_use_path = get_pgo_use_path(config);
439447
let pgo_sample_use_path = get_pgo_sample_use_path(config);
440448
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
449+
let instr_profile_output_path = get_instr_profile_output_path(config);
441450
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
442451
let sanitizer_options = if !is_lto {
443452
Some(llvm::SanitizerOptions {
@@ -488,6 +497,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
488497
pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
489498
pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
490499
config.instrument_coverage,
500+
instr_profile_output_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
491501
config.instrument_gcov,
492502
pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
493503
config.debug_info_for_profiling,

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2360,6 +2360,7 @@ extern "C" {
23602360
PGOGenPath: *const c_char,
23612361
PGOUsePath: *const c_char,
23622362
InstrumentCoverage: bool,
2363+
InstrProfileOutput: *const c_char,
23632364
InstrumentGCOV: bool,
23642365
PGOSampleUsePath: *const c_char,
23652366
DebugInfoForProfiling: bool,

compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,8 @@ LLVMRustOptimizeWithNewPassManager(
822822
bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
823823
LLVMRustSanitizerOptions *SanitizerOptions,
824824
const char *PGOGenPath, const char *PGOUsePath,
825-
bool InstrumentCoverage, bool InstrumentGCOV,
825+
bool InstrumentCoverage, const char *InstrProfileOutput,
826+
bool InstrumentGCOV,
826827
const char *PGOSampleUsePath, bool DebugInfoForProfiling,
827828
void* LlvmSelfProfiler,
828829
LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
@@ -922,8 +923,11 @@ LLVMRustOptimizeWithNewPassManager(
922923

923924
if (InstrumentCoverage) {
924925
PipelineStartEPCallbacks.push_back(
925-
[](ModulePassManager &MPM, OptimizationLevel Level) {
926+
[InstrProfileOutput](ModulePassManager &MPM, OptimizationLevel Level) {
926927
InstrProfOptions Options;
928+
if (InstrProfileOutput) {
929+
Options.InstrProfileOutput = InstrProfileOutput;
930+
}
927931
MPM.addPass(InstrProfiling(Options, false));
928932
}
929933
);

src/doc/rustc/src/instrument-coverage.md

+20-8
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,17 @@ $ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
9797
}
9898
```
9999

100-
After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`:
100+
After running this program, a new file named like `default_11699812450447639123_0_20944` should be in the current working directory.
101+
A new, unique file name will be generated each time the program is run to avoid overwriting previous data.
102+
103+
```shell
104+
$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
105+
...
106+
$ ls default_*.profraw
107+
default_11699812450447639123_0_20944.profraw
108+
```
109+
110+
You can also set a specific file name or path for the generated `.profraw` files by using the environment variable `LLVM_PROFILE_FILE`:
101111

102112
```shell
103113
$ echo "{some: 'thing'}" \
@@ -115,6 +125,9 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing
115125
- `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`).
116126
- `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered.
117127

128+
In the first example above, the value `11699812450447639123_0` in the generated filename is the instrumented binary's signature,
129+
which replaced the `%m` pattern and the value `20944` is the process ID of the binary being executed.
130+
118131
## Installing LLVM coverage tools
119132

120133
LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher, and processing the *raw* data may require exactly the LLVM version used by the compiler. (`llvm-cov --version` typically shows the tool's LLVM version number, and `rustc --verbose --version` shows the version of LLVM used by the Rust compiler.)
@@ -181,11 +194,10 @@ A typical use case for coverage analysis is test coverage. Rust's source-based c
181194

182195
The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate.
183196

184-
Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.)
197+
Since `cargo test` both builds and runs the tests, we set the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag.
185198

186199
```shell
187200
$ RUSTFLAGS="-C instrument-coverage" \
188-
LLVM_PROFILE_FILE="json5format-%m.profraw" \
189201
cargo test --tests
190202
```
191203

@@ -210,7 +222,7 @@ test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
210222
You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them:
211223

212224
```shell
213-
$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata
225+
$ llvm-profdata merge -sparse default_*.profraw -o json5format.profdata
214226
```
215227

216228
Then run the `cov` tool, with the `profdata` file and all test binaries:
@@ -230,6 +242,8 @@ $ llvm-cov show \
230242
--Xdemangler=rustfilt | less -R
231243
```
232244

245+
> **Note**: If overriding the default `profraw` file name via the `LLVM_PROFILE_FILE` environment variable, it's highly recommended to use the `%m` and `%p` special pattern strings to generate unique file names in the case of more than a single test binary being executed.
246+
233247
> **Note**: The command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results.\_
234248
235249
### Tips for listing the binaries automatically
@@ -271,9 +285,8 @@ To include doc tests in the coverage results, drop the `--tests` flag, and apply
271285
```bash
272286
$ RUSTFLAGS="-C instrument-coverage" \
273287
RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
274-
LLVM_PROFILE_FILE="json5format-%m.profraw" \
275288
cargo test
276-
$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata
289+
$ llvm-profdata merge -sparse default_*.profraw -o json5format.profdata
277290
```
278291

279292
The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries
@@ -302,8 +315,7 @@ $ llvm-cov report \
302315
> version without doc tests, include:
303316
304317
- The `cargo test ... --no-run` command is updated with the same environment variables
305-
and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE`
306-
is only used when _running_ the tests.)
318+
and flags used to _build_ the tests, _including_ the doc tests.
307319
- The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out`
308320
binaries generated for doc tests (note, however, that some `rust_out` files may not
309321
be executable binaries).
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Test that `-Cinstrument-coverage` creates expected __llvm_profile_filename symbol in LLVM IR.
2+
3+
// needs-profiler-support
4+
// compile-flags: -Cinstrument-coverage
5+
6+
// CHECK: @__llvm_profile_filename = {{.*}}"default_%m_%p.profraw\00"{{.*}}
7+
8+
#![crate_type="lib"]
9+
10+
#[inline(never)]
11+
fn some_function() {
12+
13+
}
14+
15+
pub fn some_other_function() {
16+
some_function();
17+
}

0 commit comments

Comments
 (0)