tools/run-cts: Optimize coverage collection
Enable coverage collection when using the test server, which is substantially faster than running in separate, isolated processes.
Use clang's `__llvm_profile_*` APIs to reset the counters between each test case run.
Change-Id: I01f8d0c1b3f215f66cfa50ef0fd51f2522c2ea57
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/113880
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7220a6..dd41b68 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -343,6 +343,7 @@
endif()
if (DAWN_EMIT_COVERAGE)
+ target_compile_definitions(${TARGET} PRIVATE "DAWN_EMIT_COVERAGE")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_options(${TARGET} PRIVATE "--coverage")
target_link_options(${TARGET} PRIVATE "gcov")
diff --git a/src/dawn/node/Module.cpp b/src/dawn/node/Module.cpp
index d8acf00..08344b2 100644
--- a/src/dawn/node/Module.cpp
+++ b/src/dawn/node/Module.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <filesystem>
#include <string>
#include <tuple>
#include <utility>
@@ -22,6 +23,14 @@
#include "src/dawn/node/binding/GPU.h"
#include "tint/tint.h"
+#ifdef DAWN_EMIT_COVERAGE
+extern "C" {
+void __llvm_profile_reset_counters(void);
+void __llvm_profile_set_filename(const char*);
+int __llvm_profile_write_file(void);
+}
+#endif // DAWN_EMIT_COVERAGE
+
namespace {
Napi::Value CreateGPU(const Napi::CallbackInfo& info) {
const auto& env = info.Env();
@@ -50,6 +59,32 @@
return wgpu::interop::GPU::Create<wgpu::binding::GPU>(env, std::move(flags));
}
+#ifdef DAWN_EMIT_COVERAGE
+struct Coverage {
+ Coverage() : output_path_{GetOutputPath()} {
+ __llvm_profile_set_filename(output_path_.c_str());
+ }
+ ~Coverage() { std::filesystem::remove(output_path_); }
+
+ static void Begin(const Napi::CallbackInfo& info) {
+ auto* coverage = static_cast<Coverage*>(info.Data());
+ std::filesystem::remove(coverage->output_path_);
+ __llvm_profile_reset_counters();
+ }
+
+ static Napi::Value End(const Napi::CallbackInfo& info) {
+ __llvm_profile_write_file();
+ auto* coverage = static_cast<Coverage*>(info.Data());
+ return Napi::String::New(info.Env(), coverage->output_path_.c_str());
+ }
+
+ private:
+ static std::filesystem::path GetOutputPath() { return std::tmpnam(nullptr); }
+
+ std::filesystem::path output_path_;
+};
+#endif // DAWN_EMIT_COVERAGE
+
} // namespace
// Initialize() initializes the Dawn node module, registering all the WebGPU
@@ -68,6 +103,15 @@
// Export function that creates and returns the wgpu::interop::GPU interface
exports.Set(Napi::String::New(env, "create"), Napi::Function::New<CreateGPU>(env));
+#ifdef DAWN_EMIT_COVERAGE
+ Coverage* coverage = new Coverage();
+ auto coverage_provider = Napi::Object::New(env);
+ coverage_provider.Set("begin", Napi::Function::New(env, &Coverage::Begin, nullptr, coverage));
+ coverage_provider.Set("end", Napi::Function::New(env, &Coverage::End, nullptr, coverage));
+ coverage_provider.AddFinalizer([](const Napi::Env&, Coverage* c) { delete c; }, coverage);
+ exports.Set(Napi::String::New(env, "coverage"), coverage_provider);
+#endif // DAWN_EMIT_COVERAGE
+
return exports;
}
diff --git a/tools/src/cmd/run-cts/main.go b/tools/src/cmd/run-cts/main.go
index c9f4fd9..41e604e 100644
--- a/tools/src/cmd/run-cts/main.go
+++ b/tools/src/cmd/run-cts/main.go
@@ -153,7 +153,7 @@
flag.StringVar(&adapterName, "adapter", "", "name (or substring) of the GPU adapter to use")
flag.BoolVar(&dumpShaders, "dump-shaders", false, "dump WGSL shaders. Enables --verbose")
flag.BoolVar(&unrollConstEvalLoops, "unroll-const-eval-loops", unrollConstEvalLoopsDefault, "unroll loops in const-eval tests")
- flag.BoolVar(&genCoverage, "coverage", false, "displays coverage data. Enables --isolated")
+ flag.BoolVar(&genCoverage, "coverage", false, "displays coverage data")
flag.StringVar(&coverageFile, "export-coverage", "", "write coverage data to the given path")
flag.Parse()
@@ -258,7 +258,6 @@
}
if genCoverage {
- isolated = true
llvmCov, err := exec.LookPath("llvm-cov")
if err != nil {
return fmt.Errorf("failed to find LLVM, required for --coverage")
@@ -657,6 +656,9 @@
if r.colors {
args = append(args, "--colors")
}
+ if r.covEnv != nil {
+ args = append(args, "--coverage")
+ }
if r.verbose {
args = append(args,
"--verbose",
@@ -720,8 +722,9 @@
res := result{index: idx, testcase: r.testcases[idx]}
type Response struct {
- Status string
- Message string
+ Status string
+ Message string
+ CoverageData string
}
postResp, err := http.Post(fmt.Sprintf("http://localhost:%v/run?%v", port, r.testcases[idx]), "", &bytes.Buffer{})
if err != nil {
@@ -758,6 +761,17 @@
res.status = fail
res.error = fmt.Errorf("unknown status: '%v'", resp.Status)
}
+
+ if resp.CoverageData != "" {
+ coverage, covErr := r.covEnv.Import(resp.CoverageData)
+ if covErr != nil {
+ if res.message != "" {
+ res.message += "\n"
+ }
+ res.message += fmt.Sprintf("could not import coverage data from '%v': %v", resp.CoverageData, covErr)
+ }
+ res.coverage = coverage
+ }
} else {
msg, err := ioutil.ReadAll(postResp.Body)
if err != nil {
@@ -824,6 +838,11 @@
// Once all the results have been printed, a summary will be printed and the
// function will return.
func (r *runner) streamResults(ctx context.Context, wg *sync.WaitGroup, results chan result) error {
+ // If the context was already cancelled then just return
+ if err := ctx.Err(); err != nil {
+ return err
+ }
+
// Create another goroutine to close the results chan when all the runner
// goroutines have finished.
start := time.Now()