about summary refs log tree commit diff
path: root/src/test
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-08-14 16:27:00 +0000
committerbors <bors@rust-lang.org>2020-08-14 16:27:00 +0000
commit8e21bd0633b8d970646ee6eb706c9e8acfad19af (patch)
tree3b1dc2ddb862be117ab2deb1d0ea8044d933af94 /src/test
parent55b9adfafa11b2ced5c0477c949fd875b19b3877 (diff)
parentba189788758a68a921b349e07109c18a51f18bda (diff)
downloadrust-8e21bd0633b8d970646ee6eb706c9e8acfad19af.tar.gz
rust-8e21bd0633b8d970646ee6eb706c9e8acfad19af.zip
Auto merge of #75416 - richkadel:llvm-coverage-map-gen-5.3, r=richkadel
LLVM IR coverage encoding aligns closer to Clang's

I found some areas for improvement while attempting to debug the
SegFault issue when running rust programs compiled using MSVC, with
coverage instrumentation.

I discovered that LLVM's coverage writer was generating incomplete
function name variable names (that's not a typo: the name of the
variable that holds a function name).

The existing implementation used one-up numbers to distinguish
variables, and correcting the names did not fix the MSVC coverage bug,
but the fix in this PR makes the names and resulting LLVM IR easier to
follow and more consistent with Clang's implementation.

I also changed the way the `-Zinstrument-coverage` option is supported in
symbol_export.rs. The original implementation was incorrect, and the
corrected version matches the handling for `-Zprofile-generate`, as it
turns out.

(An argument could be made that maybe `-Zinstrument-coverage` should
automatically enable `-Cprofile-generate`. In fact, if
`-Cprofile-generate` is analagous to Clang's `-fprofile-generate`, as
some documentation implies, Clang always requires this flag for its
implementation of source-based code coverage. This would require a
little more validation, and if implemented, would probably require
updating some of the user-facing messages related to
`-Cprofile-generate` to not be so specific to the PGO use case.)

None of these changes fixed the MSVC coverage problems, but they should
still be welcome improvements.

Lastly, I added some additional FIXME comments in instrument_coverage.rs
describing issues I found with the generated LLVM IR that would be
resolved if the coverage instrumentation is injected with a `Statement`
instead of as a new `BasicBlock`. I describe seven advantages of this
change, but it requires some discussion before making a change like
this.

r? @tmandry
Diffstat (limited to 'src/test')
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/Makefile78
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json2
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt51
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/testprog.rs (renamed from src/test/run-make-fulldeps/instrument-coverage/main.rs)0
-rw-r--r--src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt4
5 files changed, 116 insertions, 19 deletions
diff --git a/src/test/run-make-fulldeps/instrument-coverage/Makefile b/src/test/run-make-fulldeps/instrument-coverage/Makefile
index df47305b547..4392cfec080 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/Makefile
+++ b/src/test/run-make-fulldeps/instrument-coverage/Makefile
@@ -3,55 +3,101 @@
 
 # FIXME(richkadel): Debug the following problem, and reenable on Windows (by
 # removing the `# ignore-msvc` directive above). The current implementation
-# generates a segfault when running the instrumented `main` executable,
-# after the `main` program code executes, but before the process terminates.
-# This most likely points to a problem generating the LLVM "main.profraw"
+# generates a segfault when running the instrumented `testprog` executable,
+# after the `main()` function completes, but before the process terminates.
+# This most likely points to a problem generating the LLVM "testprog.profraw"
 # file.
 
 -include ../tools.mk
 
+UNAME = $(shell uname)
+
+ifeq ($(UNAME),Darwin)
+	INSTR_PROF_DATA_SUFFIX=,regular,live_support
+	DATA_SECTION_PREFIX=__DATA,
+	LLVM_COV_SECTION_PREFIX=__LLVM_COV,
+else
+	INSTR_PROF_DATA_SUFFIX=
+	DATA_SECTION_PREFIX=
+	LLVM_COV_SECTION_PREFIX=
+endif
+
 # This test makes sure that LLVM coverage maps are genereated in LLVM IR.
 
 COMMON_FLAGS=-Zinstrument-coverage
 
 all:
 	# Compile the test program with instrumentation, and also generate LLVM IR
-	$(RUSTC) $(COMMON_FLAGS) main.rs
+	$(RUSTC) $(COMMON_FLAGS) testprog.rs \
+			--emit=link,llvm-ir
+
+	# check the LLVM IR
+ifdef IS_WIN32
+	cat "$(TMPDIR)"/testprog.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt \
+			-check-prefixes=CHECK,WIN32 \
+			-DPRIVATE_GLOBAL="internal global" \
+			-DINSTR_PROF_DATA=".lprfd$$M" \
+			-DINSTR_PROF_NAME=".lprfn$$M" \
+			-DINSTR_PROF_CNTS=".lprfc$$M" \
+			-DINSTR_PROF_VALS=".lprfv$$M" \
+			-DINSTR_PROF_VNODES=".lprfnd$$M" \
+			-DINSTR_PROF_COVMAP=".lcovmap$$M" \
+			-DINSTR_PROF_ORDERFILE=".lorderfile$$M"
+else
+	cat "$(TMPDIR)"/testprog.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt \
+			-check-prefixes=CHECK \
+			-DPRIVATE_GLOBAL="private global" \
+			-DINSTR_PROF_DATA="$(DATA_SECTION_PREFIX)__llvm_prf_data$(INSTR_PROF_DATA_SUFFIX)" \
+			-DINSTR_PROF_NAME="$(DATA_SECTION_PREFIX)__llvm_prf_names" \
+			-DINSTR_PROF_CNTS="$(DATA_SECTION_PREFIX)__llvm_prf_cnts" \
+			-DINSTR_PROF_VALS="$(DATA_SECTION_PREFIX)__llvm_prf_vals" \
+			-DINSTR_PROF_VNODES="$(DATA_SECTION_PREFIX)__llvm_prf_vnds" \
+			-DINSTR_PROF_COVMAP="$(LLVM_COV_SECTION_PREFIX)__llvm_covmap" \
+			-DINSTR_PROF_ORDERFILE="$(DATA_SECTION_PREFIX)__llvm_orderfile"
+endif
 
 	# Run it in order to generate some profiling data,
 	# with `LLVM_PROFILE_FILE=<profdata_file>` environment variable set to
 	# output the coverage stats for this run.
-	LLVM_PROFILE_FILE="$(TMPDIR)"/main.profraw \
-	  $(call RUN,main)
+	LLVM_PROFILE_FILE="$(TMPDIR)"/testprog.profraw \
+			$(call RUN,testprog)
 
 	# Postprocess the profiling data so it can be used by the llvm-cov tool
 	"$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \
-	  "$(TMPDIR)"/main.profraw \
-		-o "$(TMPDIR)"/main.profdata
+			"$(TMPDIR)"/testprog.profraw \
+			-o "$(TMPDIR)"/testprog.profdata
 
 	# Generate a coverage report using `llvm-cov show`. The output ordering
 	# can be non-deterministic, so ignore the return status. If the test fails
 	# when comparing the JSON `export`, the `show` output may be useful when
 	# debugging.
 	"$(LLVM_BIN_DIR)"/llvm-cov show \
-	  --Xdemangler="$(RUST_DEMANGLER)" \
-	  --show-line-counts-or-regions \
-	  --instr-profile="$(TMPDIR)"/main.profdata \
-		$(call BIN,"$(TMPDIR)"/main) \
+			--Xdemangler="$(RUST_DEMANGLER)" \
+			--show-line-counts-or-regions \
+			--instr-profile="$(TMPDIR)"/testprog.profdata \
+			$(call BIN,"$(TMPDIR)"/testprog) \
 		> "$(TMPDIR)"/actual_show_coverage.txt
 
+ifdef RUSTC_BLESS_TEST
+	cp "$(TMPDIR)"/actual_show_coverage.txt typical_show_coverage.txt
+else
 	# Compare the show coverage output
 	$(DIFF) typical_show_coverage.txt "$(TMPDIR)"/actual_show_coverage.txt || \
-	  >&2 echo 'diff failed for `llvm-cov show` (might not be an error)'
+		>&2 echo 'diff failed for `llvm-cov show` (might not be an error)'
+endif
 
 	# Generate a coverage report in JSON, using `llvm-cov export`, and fail if
 	# there are differences from the expected output.
 	"$(LLVM_BIN_DIR)"/llvm-cov export \
-	  --summary-only \
-	  --instr-profile="$(TMPDIR)"/main.profdata \
-		$(call BIN,"$(TMPDIR)"/main) \
+			--summary-only \
+			--instr-profile="$(TMPDIR)"/testprog.profdata \
+			$(call BIN,"$(TMPDIR)"/testprog) \
 		| "$(PYTHON)" prettify_json.py \
 		> "$(TMPDIR)"/actual_export_coverage.json
 
+ifdef RUSTC_BLESS_TEST
+	cp "$(TMPDIR)"/actual_export_coverage.json expected_export_coverage.json
+else
 	# Check that the exported JSON coverage data matches what we expect
 	$(DIFF) expected_export_coverage.json "$(TMPDIR)"/actual_export_coverage.json
+endif
diff --git a/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json b/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json
index 9d739a89114..5881cf4b7db 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json
+++ b/src/test/run-make-fulldeps/instrument-coverage/expected_export_coverage.json
@@ -3,7 +3,7 @@
     {
       "files": [
         {
-          "filename": "main.rs",
+          "filename": "testprog.rs",
           "summary": {
             "functions": {
               "count": 7,
diff --git a/src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt b/src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt
new file mode 100644
index 00000000000..5a7cc9a1882
--- /dev/null
+++ b/src/test/run-make-fulldeps/instrument-coverage/filecheck-patterns.txt
@@ -0,0 +1,51 @@
+# Check for metadata, variables, declarations, and function definitions injected
+# into LLVM IR when compiling with -Zinstrument-coverage.
+
+WIN32:      $__llvm_profile_runtime_user = comdat any
+
+CHECK:      @__llvm_coverage_mapping = internal constant
+CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8
+
+WIN32:      @__llvm_profile_runtime = external global i32
+
+CHECK:      @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]]
+CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8
+
+CHECK:      @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = [[PRIVATE_GLOBAL]]
+CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called,
+CHECK-SAME: ()* @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called to i8*),
+CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8
+
+CHECK:      @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]]
+CHECK-SAME: section "[[INSTR_PROF_CNTS]]", align 8
+
+CHECK:      @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = [[PRIVATE_GLOBAL]]
+CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main,
+CHECK-SAME: ()* @_R{{[a-zA-Z0-9_]+}}testprog4main to i8*),
+CHECK-SAME: section "[[INSTR_PROF_DATA]]", align 8
+
+CHECK:      @__llvm_prf_nm = private constant
+CHECK-SAME: section "[[INSTR_PROF_NAME]]", align 1
+
+CHECK:      @llvm.used = appending global
+CHECK-SAME: i8* bitcast ({ {{.*}} }* @__llvm_coverage_mapping to i8*)
+WIN32-SAME: i8* bitcast (i32 ()* @__llvm_profile_runtime_user to i8*)
+CHECK-SAME: i8* bitcast ({ {{.*}} }* @__profd__R{{[a-zA-Z0-9_]*}}testprog4main to i8*)
+CHECK-SAME: i8* getelementptr inbounds ({{.*}}* @__llvm_prf_nm, i32 0, i32 0)
+CHECK-SAME: section "llvm.metadata"
+
+CHECK:      define hidden { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} {
+CHECK-NEXT: start:
+CHECK-NOT:  bb{{[0-9]+}}:
+CHECK:      %pgocount = load i64, i64* getelementptr inbounds
+CHECK-SAME: * @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called,
+
+CHECK:      declare void @llvm.instrprof.increment(i8*, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]]
+
+WIN32:      define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {
+WIN32-NEXT: %1 = load i32, i32* @__llvm_profile_runtime
+WIN32-NEXT: ret i32 %1
+WIN32-NEXT: }
+
+CHECK:      attributes #[[LLVM_INSTRPROF_INCREMENT_ATTR]] = { nounwind }
+WIN32:      attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline }
\ No newline at end of file
diff --git a/src/test/run-make-fulldeps/instrument-coverage/main.rs b/src/test/run-make-fulldeps/instrument-coverage/testprog.rs
index 358c25677ae..358c25677ae 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/main.rs
+++ b/src/test/run-make-fulldeps/instrument-coverage/testprog.rs
diff --git a/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt b/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt
index 9c593d0809d..ae123afff04 100644
--- a/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt
+++ b/src/test/run-make-fulldeps/instrument-coverage/typical_show_coverage.txt
@@ -25,14 +25,14 @@
    25|      2|    }
    26|      2|}
   ------------------
-  | main[317d481089b8c8fe]::wrap_with::<main[317d481089b8c8fe]::main::{closure#0}, &str>:
+  | testprog[317d481089b8c8fe]::wrap_with::<testprog[317d481089b8c8fe]::main::{closure#0}, &str>:
   |   22|      1|{
   |   23|      1|    if should_wrap {
   |   24|      1|        wrapper(&inner)
   |   25|      1|    }
   |   26|      1|}
   ------------------
-  | main[317d481089b8c8fe]::wrap_with::<main[317d481089b8c8fe]::main::{closure#1}, &str>:
+  | testprog[317d481089b8c8fe]::wrap_with::<testprog[317d481089b8c8fe]::main::{closure#1}, &str>:
   |   22|      1|{
   |   23|      1|    if should_wrap {
   |   24|      1|        wrapper(&inner)