From 100882296e065526cfd055380e14334738da4158 Mon Sep 17 00:00:00 2001 From: ridwanabdillahi <91507758+ridwanabdillahi@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:16:20 -0700 Subject: [PATCH 1/3] Add support for generating unique *.profraw files by default when using the `-C instrument-coverage` flag. Respond to PR comments. --- compiler/rustc_codegen_llvm/src/back/write.rs | 10 ++++++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 8 ++++++-- src/test/codegen/instrument-coverage.rs | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/test/codegen/instrument-coverage.rs diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 534d32e8a90e9..879baaa8556f5 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -423,6 +423,14 @@ fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option { .map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap()) } +fn get_instr_profile_output_path(config: &ModuleConfig) -> Option { + if config.instrument_coverage { + Some(CString::new(format!("{}", PathBuf::from("default_%m_%p.profraw").display())).unwrap()) + } else { + None + } +} + pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( cgcx: &CodegenContext, diag_handler: &Handler, @@ -438,6 +446,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( let pgo_use_path = get_pgo_use_path(config); let pgo_sample_use_path = get_pgo_sample_use_path(config); let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO; + let instr_profile_output_path = get_instr_profile_output_path(config); // Sanitizer instrumentation is only inserted during the pre-link optimization stage. let sanitizer_options = if !is_lto { Some(llvm::SanitizerOptions { @@ -488,6 +497,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), 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, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 3139f93bfefae..0d27614b9a3f7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2347,6 +2347,7 @@ extern "C" { PGOGenPath: *const c_char, PGOUsePath: *const c_char, InstrumentCoverage: bool, + InstrProfileOutput: *const c_char, InstrumentGCOV: bool, PGOSampleUsePath: *const c_char, DebugInfoForProfiling: bool, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 0a6bd49992d99..bdf2a85250730 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -822,7 +822,8 @@ LLVMRustOptimizeWithNewPassManager( bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, - bool InstrumentCoverage, bool InstrumentGCOV, + bool InstrumentCoverage, const char *InstrProfileOutput, + bool InstrumentGCOV, const char *PGOSampleUsePath, bool DebugInfoForProfiling, void* LlvmSelfProfiler, LLVMRustSelfProfileBeforePassCallback BeforePassCallback, @@ -922,8 +923,11 @@ LLVMRustOptimizeWithNewPassManager( if (InstrumentCoverage) { PipelineStartEPCallbacks.push_back( - [](ModulePassManager &MPM, OptimizationLevel Level) { + [InstrProfileOutput](ModulePassManager &MPM, OptimizationLevel Level) { InstrProfOptions Options; + if (InstrProfileOutput) { + Options.InstrProfileOutput = InstrProfileOutput; + } MPM.addPass(InstrProfiling(Options, false)); } ); diff --git a/src/test/codegen/instrument-coverage.rs b/src/test/codegen/instrument-coverage.rs new file mode 100644 index 0000000000000..78f8875a2d90c --- /dev/null +++ b/src/test/codegen/instrument-coverage.rs @@ -0,0 +1,17 @@ +// Test that `-Cinstrument-coverage` creates expected __llvm_profile_filename symbol in LLVM IR. + +// needs-profiler-support +// compile-flags: -Cinstrument-coverage + +// CHECK: @__llvm_profile_filename = {{.*}}"default_%m_%p.profraw\00"{{.*}} + +#![crate_type="lib"] + +#[inline(never)] +fn some_function() { + +} + +pub fn some_other_function() { + some_function(); +} From 804579ca7716bbe8125481bffde555e5680b8824 Mon Sep 17 00:00:00 2001 From: ridwanabdillahi <91507758+ridwanabdillahi@users.noreply.github.com> Date: Fri, 12 Aug 2022 11:34:31 -0700 Subject: [PATCH 2/3] Respond to RFC comments. --- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- src/doc/rustc/src/instrument-coverage.md | 40 +++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 879baaa8556f5..0913715b3c0b8 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -425,7 +425,7 @@ fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option { fn get_instr_profile_output_path(config: &ModuleConfig) -> Option { if config.instrument_coverage { - Some(CString::new(format!("{}", PathBuf::from("default_%m_%p.profraw").display())).unwrap()) + Some(CString::new("default_%m_%p.profraw").unwrap()) } else { None } diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 0ae9e53af3cd2..e1340c893f9a1 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -97,7 +97,24 @@ $ echo "{some: 'thing'}" | target/debug/examples/formatjson5 - } ``` -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`: +After running this program, a new file, `default_%m_%p.profraw`, should be in the current working directory. This file takes advantage ofLLVM's support for rewriting special pattern strings to ensure `.profraw` files generated are unique. The following special pattern strings are rewritten as: + +- `%p` - The process ID. +- `%h` - The hostname of the machine running the program. +- `%t` - The value of the TMPDIR environment variable. +- `%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`). +- `%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. + +```shell +$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 - +... +$ ls default_11699812450447639123_0_20944.profraw +default_11699812450447639123_0_20944.profraw +``` + +In the example above, the value `11699812450447639123_0` in the generated filename is the instrumented binary's signature, which replaced the `%m` pattern and the value `20944` is the process ID of the binary being executed. + +You can also set a specific file name or path for the generated `.profraw` files by using the environment variable `LLVM_PROFILE_FILE`: ```shell $ echo "{some: 'thing'}" \ @@ -107,14 +124,6 @@ $ ls formatjson5.profraw formatjson5.profraw ``` -If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten: - -- `%p` - The process ID. -- `%h` - The hostname of the machine running the program. -- `%t` - The value of the TMPDIR environment variable. -- `%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`). -- `%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. - ## Installing LLVM coverage tools 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 +190,12 @@ A typical use case for coverage analysis is test coverage. Rust's source-based c The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate. -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.) +Since `cargo test` both builds and runs the tests, we set the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag. If setting `LLVM_PROFILE_FILE` to specify a custom filename for the raw profiling data generated during the test runs, +apply `%m` in the filename pattern since there may be more than one test binary. This generates unique names for each test binary which is not done by default when setting the `LLVM_PROFILE_FILE` environment variable. +(Otherwise, each executed test binary would overwrite the coverage results from the previous binary.) If not setting `LLVM_PROFILE_FILE`, the `%m` and `%p` filename patterns are added by default. ```shell $ RUSTFLAGS="-C instrument-coverage" \ - LLVM_PROFILE_FILE="json5format-%m.profraw" \ cargo test --tests ``` @@ -210,7 +220,7 @@ test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them: ```shell -$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata +$ llvm-profdata merge -sparse default_*.profraw -o json5format.profdata ``` Then run the `cov` tool, with the `profdata` file and all test binaries: @@ -271,9 +281,8 @@ To include doc tests in the coverage results, drop the `--tests` flag, and apply ```bash $ RUSTFLAGS="-C instrument-coverage" \ RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ - LLVM_PROFILE_FILE="json5format-%m.profraw" \ cargo test -$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata +$ llvm-profdata merge -sparse default_*.profraw -o json5format.profdata ``` The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries @@ -302,8 +311,7 @@ $ llvm-cov report \ > version without doc tests, include: - The `cargo test ... --no-run` command is updated with the same environment variables - and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE` - is only used when _running_ the tests.) + and flags used to _build_ the tests, _including_ the doc tests. - The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out` binaries generated for doc tests (note, however, that some `rust_out` files may not be executable binaries). From 7e49c1b58fc68cd4f7d784aa5ab09bd699ac01f1 Mon Sep 17 00:00:00 2001 From: ridwanabdillahi <91507758+ridwanabdillahi@users.noreply.github.com> Date: Fri, 12 Aug 2022 13:24:17 -0700 Subject: [PATCH 3/3] Respond to PR comments. Cleanup documentation. --- src/doc/rustc/src/instrument-coverage.md | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index e1340c893f9a1..38fd5c9699763 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -97,23 +97,16 @@ $ echo "{some: 'thing'}" | target/debug/examples/formatjson5 - } ``` -After running this program, a new file, `default_%m_%p.profraw`, should be in the current working directory. This file takes advantage ofLLVM's support for rewriting special pattern strings to ensure `.profraw` files generated are unique. The following special pattern strings are rewritten as: - -- `%p` - The process ID. -- `%h` - The hostname of the machine running the program. -- `%t` - The value of the TMPDIR environment variable. -- `%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`). -- `%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. +After running this program, a new file named like `default_11699812450447639123_0_20944` should be in the current working directory. +A new, unique file name will be generated each time the program is run to avoid overwriting previous data. ```shell $ echo "{some: 'thing'}" | target/debug/examples/formatjson5 - ... -$ ls default_11699812450447639123_0_20944.profraw +$ ls default_*.profraw default_11699812450447639123_0_20944.profraw ``` -In the example above, the value `11699812450447639123_0` in the generated filename is the instrumented binary's signature, which replaced the `%m` pattern and the value `20944` is the process ID of the binary being executed. - You can also set a specific file name or path for the generated `.profraw` files by using the environment variable `LLVM_PROFILE_FILE`: ```shell @@ -124,6 +117,17 @@ $ ls formatjson5.profraw formatjson5.profraw ``` +If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten: + +- `%p` - The process ID. +- `%h` - The hostname of the machine running the program. +- `%t` - The value of the TMPDIR environment variable. +- `%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`). +- `%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. + +In the first example above, the value `11699812450447639123_0` in the generated filename is the instrumented binary's signature, +which replaced the `%m` pattern and the value `20944` is the process ID of the binary being executed. + ## Installing LLVM coverage tools 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.) @@ -190,9 +194,7 @@ A typical use case for coverage analysis is test coverage. Rust's source-based c The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate. -Since `cargo test` both builds and runs the tests, we set the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag. If setting `LLVM_PROFILE_FILE` to specify a custom filename for the raw profiling data generated during the test runs, -apply `%m` in the filename pattern since there may be more than one test binary. This generates unique names for each test binary which is not done by default when setting the `LLVM_PROFILE_FILE` environment variable. -(Otherwise, each executed test binary would overwrite the coverage results from the previous binary.) If not setting `LLVM_PROFILE_FILE`, the `%m` and `%p` filename patterns are added by default. +Since `cargo test` both builds and runs the tests, we set the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag. ```shell $ RUSTFLAGS="-C instrument-coverage" \ @@ -240,6 +242,8 @@ $ llvm-cov show \ --Xdemangler=rustfilt | less -R ``` +> **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. + > **Note**: The command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results.\_ ### Tips for listing the binaries automatically