tools: Port turbo-cov from SwiftShader

A tiny utility that emits just the per segment coverage in a binary stream. This avoids the overhead of encoding to JSON, which provides substantial performance improvements.

Change-Id: I36a588069d69c5c800d31bca8dd5c542bcdbe313
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/113641
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f70f432..27d70f2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -137,6 +137,7 @@
 option_if_not_defined(DAWN_ENABLE_PIC "Build with Position-Independent-Code enabled" OFF)
 
 option_if_not_defined(DAWN_EMIT_COVERAGE "Emit code coverage information" OFF)
+set_if_not_defined(LLVM_SOURCE_DIR "${Dawn_LLVM_SOURCE_DIR}" "Directory to an LLVM source checkout. Required to build turbo-cov")
 
 if (DAWN_ENABLE_OPENGLES OR DAWN_ENABLE_DESKTOP_GL)
   set(TINT_DEFAULT_GLSL ON)
diff --git a/tools/src/cmd/turbo-cov/CMakeLists.txt b/tools/src/cmd/turbo-cov/CMakeLists.txt
new file mode 100644
index 0000000..f1981b9
--- /dev/null
+++ b/tools/src/cmd/turbo-cov/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright 2022 The Tint and Dawn Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+project(turbo-cov)
+
+if (LLVM_SOURCE_DIR)
+    set(LLVM_INCLUDE_TESTS OFF)
+    set(LLVM_TARGETS_TO_BUILD "X86") # At least one target needs to be provided
+    add_subdirectory("${LLVM_SOURCE_DIR}" "llvm" EXCLUDE_FROM_ALL)
+
+    add_executable(turbo-cov
+        main.cpp
+    )
+
+    set_target_properties(turbo-cov PROPERTIES
+        FOLDER "Tests"
+        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
+        INCLUDE_DIRECTORIES "${LLVM_BINARY_DIR}/include;${LLVM_SOURCE_DIR}/include"
+    )
+
+    target_link_libraries(turbo-cov
+        PRIVATE
+            LLVMProfileData
+            LLVMCoverage
+    )
+else (LLVM_SOURCE_DIR)
+    message("not building turbo-cov as LLVM_SOURCE_DIR is undefined")
+endif (LLVM_SOURCE_DIR)
diff --git a/tools/src/cmd/turbo-cov/main.cpp b/tools/src/cmd/turbo-cov/main.cpp
new file mode 100644
index 0000000..07e4aa5
--- /dev/null
+++ b/tools/src/cmd/turbo-cov/main.cpp
@@ -0,0 +1,99 @@
+// Copyright 2022 The Tint and Dawn Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// turbo-cov is a minimal re-implementation of LLVM's llvm-cov, that emits just
+// the per segment coverage in a binary stream. This avoids the overhead of
+// encoding to JSON.
+
+#include <cstdio>
+#include <utility>
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/ProfileData/InstrProfReader.h"
+
+namespace {
+
+template <typename T>
+void emit(T v) {
+    fwrite(&v, sizeof(v), 1, stdout);
+}
+
+void emit(const llvm::StringRef& str) {
+    uint64_t len = str.size();
+    emit<uint32_t>(len);
+    fwrite(str.data(), len, 1, stdout);
+}
+
+}  // namespace
+
+int main(int argc, const char** argv) {
+    if (argc < 3) {
+        fprintf(stderr, "turbo-cov <exe> <profdata>\n");
+        return 1;
+    }
+
+    auto exe = argv[1];
+    auto profdata = argv[2];
+
+    auto res = llvm::coverage::CoverageMapping::load({exe}, profdata);
+    if (auto E = res.takeError()) {
+        fprintf(stderr, "failed to load executable '%s': %s\n", exe,
+                llvm::toString(std::move(E)).c_str());
+        return 1;
+    }
+
+    auto coverage = std::move(res.get());
+    if (!coverage) {
+        fprintf(stderr, "failed to load coverage information\n");
+        return 1;
+    }
+
+    if (auto mismatched = coverage->getMismatchedCount()) {
+        fprintf(stderr, "%d functions have mismatched data\n", static_cast<int>(mismatched));
+        return 1;
+    }
+
+    // uint32 num_files
+    //   file[0]
+    //     uint32 filename.length
+    //     <data> filename.data
+    //     uint32 num_segments
+    //       file[0].segment[0]
+    //         uint32 line
+    //         uint32 col
+    //         uint32 count
+    //         uint8  hasCount
+    //       file[0].segment[1]
+    //         ...
+    //   file[2]
+    //     ...
+
+    auto files = coverage->getUniqueSourceFiles();
+    emit<uint32_t>(files.size());
+    for (auto& file : files) {
+        emit(file);
+        auto fileCoverage = coverage->getCoverageForFile(file);
+        emit<uint32_t>(fileCoverage.end() - fileCoverage.begin());
+        for (auto& segment : fileCoverage) {
+            emit<uint32_t>(segment.Line);
+            emit<uint32_t>(segment.Col);
+            emit<uint32_t>(segment.Count);
+            emit<uint8_t>(segment.HasCount ? 1 : 0);
+        }
+    }
+
+    return 0;
+}