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;
+}