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>
This commit is contained in:
parent
df2c1621bf
commit
54264d3037
|
@ -343,6 +343,7 @@ function(common_compile_options TARGET)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (DAWN_EMIT_COVERAGE)
|
if (DAWN_EMIT_COVERAGE)
|
||||||
|
target_compile_definitions(${TARGET} PRIVATE "DAWN_EMIT_COVERAGE")
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
target_compile_options(${TARGET} PRIVATE "--coverage")
|
target_compile_options(${TARGET} PRIVATE "--coverage")
|
||||||
target_link_options(${TARGET} PRIVATE "gcov")
|
target_link_options(${TARGET} PRIVATE "gcov")
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -22,6 +23,14 @@
|
||||||
#include "src/dawn/node/binding/GPU.h"
|
#include "src/dawn/node/binding/GPU.h"
|
||||||
#include "tint/tint.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 {
|
namespace {
|
||||||
Napi::Value CreateGPU(const Napi::CallbackInfo& info) {
|
Napi::Value CreateGPU(const Napi::CallbackInfo& info) {
|
||||||
const auto& env = info.Env();
|
const auto& env = info.Env();
|
||||||
|
@ -50,6 +59,32 @@ Napi::Value CreateGPU(const Napi::CallbackInfo& info) {
|
||||||
return wgpu::interop::GPU::Create<wgpu::binding::GPU>(env, std::move(flags));
|
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
|
} // namespace
|
||||||
|
|
||||||
// Initialize() initializes the Dawn node module, registering all the WebGPU
|
// Initialize() initializes the Dawn node module, registering all the WebGPU
|
||||||
|
@ -68,6 +103,15 @@ Napi::Object Initialize(Napi::Env env, Napi::Object exports) {
|
||||||
// Export function that creates and returns the wgpu::interop::GPU interface
|
// Export function that creates and returns the wgpu::interop::GPU interface
|
||||||
exports.Set(Napi::String::New(env, "create"), Napi::Function::New<CreateGPU>(env));
|
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;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ func run() error {
|
||||||
flag.StringVar(&adapterName, "adapter", "", "name (or substring) of the GPU adapter to use")
|
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(&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(&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.StringVar(&coverageFile, "export-coverage", "", "write coverage data to the given path")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -258,7 +258,6 @@ func run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if genCoverage {
|
if genCoverage {
|
||||||
isolated = true
|
|
||||||
llvmCov, err := exec.LookPath("llvm-cov")
|
llvmCov, err := exec.LookPath("llvm-cov")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find LLVM, required for --coverage")
|
return fmt.Errorf("failed to find LLVM, required for --coverage")
|
||||||
|
@ -657,6 +656,9 @@ func (r *runner) runServer(ctx context.Context, id int, caseIndices <-chan int,
|
||||||
if r.colors {
|
if r.colors {
|
||||||
args = append(args, "--colors")
|
args = append(args, "--colors")
|
||||||
}
|
}
|
||||||
|
if r.covEnv != nil {
|
||||||
|
args = append(args, "--coverage")
|
||||||
|
}
|
||||||
if r.verbose {
|
if r.verbose {
|
||||||
args = append(args,
|
args = append(args,
|
||||||
"--verbose",
|
"--verbose",
|
||||||
|
@ -720,8 +722,9 @@ func (r *runner) runServer(ctx context.Context, id int, caseIndices <-chan int,
|
||||||
res := result{index: idx, testcase: r.testcases[idx]}
|
res := result{index: idx, testcase: r.testcases[idx]}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Status string
|
Status string
|
||||||
Message string
|
Message string
|
||||||
|
CoverageData string
|
||||||
}
|
}
|
||||||
postResp, err := http.Post(fmt.Sprintf("http://localhost:%v/run?%v", port, r.testcases[idx]), "", &bytes.Buffer{})
|
postResp, err := http.Post(fmt.Sprintf("http://localhost:%v/run?%v", port, r.testcases[idx]), "", &bytes.Buffer{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -758,6 +761,17 @@ func (r *runner) runServer(ctx context.Context, id int, caseIndices <-chan int,
|
||||||
res.status = fail
|
res.status = fail
|
||||||
res.error = fmt.Errorf("unknown status: '%v'", resp.Status)
|
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 {
|
} else {
|
||||||
msg, err := ioutil.ReadAll(postResp.Body)
|
msg, err := ioutil.ReadAll(postResp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -824,6 +838,11 @@ func (r *runner) runParallelIsolated(ctx context.Context) error {
|
||||||
// Once all the results have been printed, a summary will be printed and the
|
// Once all the results have been printed, a summary will be printed and the
|
||||||
// function will return.
|
// function will return.
|
||||||
func (r *runner) streamResults(ctx context.Context, wg *sync.WaitGroup, results chan result) error {
|
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
|
// Create another goroutine to close the results chan when all the runner
|
||||||
// goroutines have finished.
|
// goroutines have finished.
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
Loading…
Reference in New Issue