Replace virtual includes generated paths with their actual paths in the coverage reports.

Fixes #6254.

RELNOTES: The code coverage report now includes the actual paths to header files instead of the ugly,
Bazel generated, virtual includes path.

For example, the `SF:` (source file) lines in the coverage report now look like this:
```
SF:include/common/strategy.h
```
instead of
```
SF:bazel-out/k8-fastbuild/bin/include/common/_virtual_includes/strategy/strategy.h
```

Closes #6372.

PiperOrigin-RevId: 219112911
diff --git a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/BUILD b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/BUILD
index 9f80e95..2e93c25 100644
--- a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/BUILD
+++ b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/BUILD
@@ -104,6 +104,7 @@
     deps = [
         ":Constants",
         ":SourceFileCoverage",
+        "//third_party:guava",
     ],
 )
 
diff --git a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Coverage.java b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Coverage.java
index 196723c..25c1851 100644
--- a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Coverage.java
+++ b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Coverage.java
@@ -16,6 +16,8 @@
 
 import static com.google.devtools.coverageoutputgenerator.Constants.CC_EXTENSIONS;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -86,6 +88,23 @@
     return false;
   }
 
+  /**
+   * Replaces the source file names in the current coverage with their mapping in the given map, if
+   * it exists.
+   */
+  void maybeReplaceSourceFileNames(ImmutableMap<String, String> reportedToOriginalSources) {
+    Preconditions.checkNotNull(reportedToOriginalSources);
+    if (reportedToOriginalSources.isEmpty()) {
+      // nothing to replace
+      return;
+    }
+    for (SourceFileCoverage source : this.getAllSourceFiles()) {
+      if (reportedToOriginalSources.containsKey(source.sourceFileName())) {
+        source.changeSourcefileName(reportedToOriginalSources.get(source.sourceFileName()));
+      }
+    }
+  }
+
   static Coverage filterOutMatchingSources(Coverage coverage, List<String> regexes)
       throws IllegalArgumentException {
     if (coverage == null || regexes == null) {
diff --git a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/LcovMergerFlags.java b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/LcovMergerFlags.java
index cbf3329..798b737 100644
--- a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/LcovMergerFlags.java
+++ b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/LcovMergerFlags.java
@@ -42,6 +42,9 @@
   @Nullable
   abstract String sourceFileManifest();
 
+  @Nullable
+  abstract String sourcesToReplaceFile();
+
   /** Parse flags in the form of "--coverage_dir=... -output_file=..." */
   static LcovMergerFlags parseFlags(String[] args) {
     ImmutableList.Builder<String> filterSources = new ImmutableList.Builder<>();
@@ -49,6 +52,7 @@
     String reportsFile = null;
     String outputFile = null;
     String sourceFileManifest = null;
+    String sourcesToReplaceFile = null;
 
     for (String arg : args) {
       if (!arg.startsWith("--")) {
@@ -74,6 +78,9 @@
         case "source_file_manifest":
           sourceFileManifest = parts[1];
           break;
+        case "sources_to_replace_file":
+          sourcesToReplaceFile = parts[1];
+          break;
         default:
           throw new IllegalArgumentException("Unknown flag " + arg);
       }
@@ -91,7 +98,12 @@
       throw new IllegalArgumentException("--output_file was not specified.");
     }
     return new AutoValue_LcovMergerFlags(
-        coverageDir, reportsFile, outputFile, filterSources.build(), sourceFileManifest);
+        coverageDir,
+        reportsFile,
+        outputFile,
+        filterSources.build(),
+        sourceFileManifest,
+        sourcesToReplaceFile);
   }
 
   boolean hasSourceFileManifest() {
diff --git a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Main.java b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Main.java
index e49e39f..1d215c1 100644
--- a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Main.java
+++ b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/Main.java
@@ -21,6 +21,7 @@
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
@@ -64,6 +65,10 @@
             parseFiles(getTracefiles(flags, filesInCoverageDir), LcovParser::parse),
             parseFiles(getGcovInfoFiles(filesInCoverageDir), GcovParser::parse));
 
+    if (flags.sourcesToReplaceFile() != null) {
+      coverage.maybeReplaceSourceFileNames(getMapFromFile(flags.sourcesToReplaceFile()));
+    }
+
     File profdataFile = getProfdataFileOrNull(filesInCoverageDir);
     if (coverage.isEmpty()) {
       int exitStatus = 0;
@@ -215,6 +220,32 @@
     return lcovTracefiles;
   }
 
+  /**
+   * Reads the content of the given file and returns a matching map.
+   *
+   * <p>It assumes the file contains lines in the form key:value. For each line it creates an entry
+   * in the map with the corresponding key and value.
+   */
+  private static ImmutableMap<String, String> getMapFromFile(String file) {
+    ImmutableMap.Builder<String, String> mapBuilder = ImmutableMap.builder();
+
+    try (FileInputStream inputStream = new FileInputStream(file);
+        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, UTF_8);
+        BufferedReader reader = new BufferedReader(inputStreamReader)) {
+      for (String keyToValueLine = reader.readLine();
+          keyToValueLine != null;
+          keyToValueLine = reader.readLine()) {
+        String[] keyAndValue = keyToValueLine.split(":");
+        if (keyAndValue.length == 2) {
+          mapBuilder.put(keyAndValue[0], keyAndValue[1]);
+        }
+      }
+    } catch (IOException e) {
+      logger.log(Level.SEVERE, "Error reading file " + file + ": " + e.getMessage());
+    }
+    return mapBuilder.build();
+  }
+
   private static Coverage parseFiles(List<File> files, Parser parser) {
     Coverage coverage = new Coverage();
     for (File file : files) {
diff --git a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/SourceFileCoverage.java b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/SourceFileCoverage.java
index d7c5f47..a1a0275 100644
--- a/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/SourceFileCoverage.java
+++ b/tools/test/CoverageOutputGenerator/java/com/google/devtools/coverageoutputgenerator/SourceFileCoverage.java
@@ -28,7 +28,7 @@
  */
 class SourceFileCoverage {
 
-  private final String sourceFileName;
+  private String sourceFileName;
   private final TreeMap<String, Integer> lineNumbers; // function name to line numbers
   private final TreeMap<String, Integer> functionsExecution; // function name to execution count
   private final TreeMap<Integer, BranchCoverage> branches; // line number to branch
@@ -56,6 +56,10 @@
     this.lines.putAll(other.lines);
   }
 
+  void changeSourcefileName(String newSourcefileName) {
+    this.sourceFileName = newSourcefileName;
+  }
+
   /*
    * Returns the merged functions found in the two given {@code SourceFileCoverage}s.
    */
diff --git a/tools/test/collect_coverage.sh b/tools/test/collect_coverage.sh
index bcdf8fd..a31e083 100755
--- a/tools/test/collect_coverage.sh
+++ b/tools/test/collect_coverage.sh
@@ -103,6 +103,7 @@
     eval "${CC_CODE_COVERAGE_SCRIPT}"
 fi
 
+
 # Export the command line that invokes LcovMerger with the flags:
 # --coverage_dir          The absolute path of the directory where the
 #                         intermediate coverage reports are located.
@@ -124,7 +125,7 @@
 #                         keep only the C++ sources found in the manifest.
 #                         For other languages the sources in the manifest are
 #                         ignored.
-export LCOV_MERGER_CMD="${LCOV_MERGER} --coverage_dir=${COVERAGE_DIR} \
+LCOV_MERGER_CMD="${LCOV_MERGER} --coverage_dir=${COVERAGE_DIR} \
   --output_file=${COVERAGE_OUTPUT_FILE} \
   --filter_sources=/usr/bin/.+ \
   --filter_sources=/usr/lib/.+ \
@@ -132,6 +133,10 @@
   --filter_sources=.*external/.+ \
   --source_file_manifest=${COVERAGE_MANIFEST}"
 
+if [[ $COVERAGE_REPORTED_TO_ACTUAL_SOURCES_FILE ]]; then
+  LCOV_MERGER_CMD="$LCOV_MERGER_CMD\
+  --sources_to_replace_file=$ROOT/$COVERAGE_REPORTED_TO_ACTUAL_SOURCES_FILE"
+fi
 
 if [[ $DISPLAY_LCOV_CMD ]] ; then
   echo "Running lcov_merger"