code-coverage / turbocov fixes
Common: * The turbocov build target (somehow) never got hooked up to the root CMakeLists.txt file. This is now fixed. macOS: * Emit coverage for 'AppleClang' compiler. * Have run-cts find the tucked-away `llvm-profdata` executable. Windows: * Various fixes for building with clang * Fix turbocov stdout CRLF corruption * Fix bad JSON with backslashes Change-Id: I481cceafe2e72b544e13168172fc1456e5df2005 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117880 Reviewed-by: Antonio Maiorano <amaiorano@google.com> Kokoro: Ben Clayton <bclayton@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
b4049dcf40
commit
78e4530f59
|
@ -347,7 +347,7 @@ function(common_compile_options TARGET)
|
||||||
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")
|
||||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
elseif(COMPILER_IS_CLANG OR COMPILER_IS_CLANG_CL)
|
||||||
target_compile_options(${TARGET} PRIVATE "-fprofile-instr-generate" "-fcoverage-mapping")
|
target_compile_options(${TARGET} PRIVATE "-fprofile-instr-generate" "-fcoverage-mapping")
|
||||||
target_link_options(${TARGET} PRIVATE "-fprofile-instr-generate" "-fcoverage-mapping")
|
target_link_options(${TARGET} PRIVATE "-fprofile-instr-generate" "-fcoverage-mapping")
|
||||||
else()
|
else()
|
||||||
|
@ -451,10 +451,13 @@ if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_SIMULATE_ID STREQUAL
|
||||||
set(COMPILER_IS_CLANG_CL TRUE)
|
set(COMPILER_IS_CLANG_CL TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR
|
if((CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR
|
||||||
(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR
|
|
||||||
((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND
|
((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND
|
||||||
(NOT COMPILER_IS_CLANG_CL)))
|
(NOT COMPILER_IS_CLANG_CL)))
|
||||||
|
set(COMPILER_IS_CLANG TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR COMPILER_IS_CLANG)
|
||||||
set(COMPILER_IS_LIKE_GNU TRUE)
|
set(COMPILER_IS_LIKE_GNU TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -555,8 +558,7 @@ function(tint_default_compile_options TARGET)
|
||||||
${COMMON_GNU_OPTIONS}
|
${COMMON_GNU_OPTIONS}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR
|
if (COMPILER_IS_CLANG)
|
||||||
("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang"))
|
|
||||||
target_compile_options(${TARGET} PRIVATE
|
target_compile_options(${TARGET} PRIVATE
|
||||||
${COMMON_CLANG_OPTIONS}
|
${COMMON_CLANG_OPTIONS}
|
||||||
)
|
)
|
||||||
|
@ -655,9 +657,11 @@ add_custom_target(tint-format
|
||||||
COMMENT "Running formatter"
|
COMMENT "Running formatter"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
||||||
|
if (DAWN_EMIT_COVERAGE)
|
||||||
|
add_subdirectory(tools/src/cmd/turbo-cov)
|
||||||
|
|
||||||
if (DAWN_EMIT_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
# The tint-generate-coverage target generates a lcov.info file at the project
|
||||||
# Generates a lcov.info file at the project root.
|
# root, holding the code coverage for all the tint_unitests.
|
||||||
# This can be used by tools such as VSCode's Coverage Gutters extension to
|
# This can be used by tools such as VSCode's Coverage Gutters extension to
|
||||||
# visualize code coverage in the editor.
|
# visualize code coverage in the editor.
|
||||||
get_filename_component(CLANG_BIN_DIR ${CMAKE_C_COMPILER} DIRECTORY)
|
get_filename_component(CLANG_BIN_DIR ${CMAKE_C_COMPILER} DIRECTORY)
|
||||||
|
|
|
@ -102,8 +102,14 @@ if (WIN32)
|
||||||
# Generate the NapiSymbols.lib from the NapiSymbols.def file
|
# Generate the NapiSymbols.lib from the NapiSymbols.def file
|
||||||
set(NAPI_SYMBOLS_LIB "${DAWN_NODE_GEN_DIR}/NapiSymbols.lib")
|
set(NAPI_SYMBOLS_LIB "${DAWN_NODE_GEN_DIR}/NapiSymbols.lib")
|
||||||
# Resolve path to lib.exe
|
# Resolve path to lib.exe
|
||||||
get_filename_component(VS_BIN_DIR "${CMAKE_LINKER}" DIRECTORY)
|
get_filename_component(LINKER_BIN_DIR "${CMAKE_LINKER}" DIRECTORY)
|
||||||
set(LIB_EXE "${VS_BIN_DIR}/lib.exe")
|
if (EXISTS "${LINKER_BIN_DIR}/lib.exe")
|
||||||
|
set(LIB_EXE "${LINKER_BIN_DIR}/lib.exe")
|
||||||
|
elseif (EXISTS "${LINKER_BIN_DIR}/lld-link.exe")
|
||||||
|
set(LIB_EXE "${LINKER_BIN_DIR}/lld-link.exe")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "unable to find lib.exe or lld-link.exe")
|
||||||
|
endif()
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
COMMAND "${LIB_EXE}"
|
COMMAND "${LIB_EXE}"
|
||||||
"/DEF:${NAPI_SYMBOLS_DEF}"
|
"/DEF:${NAPI_SYMBOLS_DEF}"
|
||||||
|
|
|
@ -62,7 +62,7 @@ Napi::Value CreateGPU(const Napi::CallbackInfo& info) {
|
||||||
#ifdef DAWN_EMIT_COVERAGE
|
#ifdef DAWN_EMIT_COVERAGE
|
||||||
struct Coverage {
|
struct Coverage {
|
||||||
Coverage() : output_path_{GetOutputPath()} {
|
Coverage() : output_path_{GetOutputPath()} {
|
||||||
__llvm_profile_set_filename(output_path_.c_str());
|
__llvm_profile_set_filename(output_path_.string().c_str());
|
||||||
}
|
}
|
||||||
~Coverage() { std::filesystem::remove(output_path_); }
|
~Coverage() { std::filesystem::remove(output_path_); }
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ struct Coverage {
|
||||||
static Napi::Value End(const Napi::CallbackInfo& info) {
|
static Napi::Value End(const Napi::CallbackInfo& info) {
|
||||||
__llvm_profile_write_file();
|
__llvm_profile_write_file();
|
||||||
auto* coverage = static_cast<Coverage*>(info.Data());
|
auto* coverage = static_cast<Coverage*>(info.Data());
|
||||||
return Napi::String::New(info.Env(), coverage->output_path_.c_str());
|
return Napi::String::New(info.Env(), coverage->output_path_.string().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -259,17 +259,36 @@ func run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if genCoverage {
|
if genCoverage {
|
||||||
llvmCov, err := exec.LookPath("llvm-cov")
|
dawnOutDir := filepath.Dir(dawnNode)
|
||||||
|
|
||||||
|
profdata, err := exec.LookPath("llvm-profdata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find LLVM, required for --coverage")
|
profdata = ""
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
profdata = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/llvm-profdata"
|
||||||
|
if !fileutils.IsExe(profdata) {
|
||||||
|
profdata = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
turboCov := filepath.Join(filepath.Dir(dawnNode), "turbo-cov"+fileutils.ExeExt)
|
if profdata == "" {
|
||||||
|
return fmt.Errorf("failed to find llvm-profdata, required for --coverage")
|
||||||
|
}
|
||||||
|
|
||||||
|
llvmCov := ""
|
||||||
|
turboCov := filepath.Join(dawnOutDir, "turbo-cov"+fileutils.ExeExt)
|
||||||
if !fileutils.IsExe(turboCov) {
|
if !fileutils.IsExe(turboCov) {
|
||||||
turboCov = ""
|
turboCov = ""
|
||||||
|
if path, err := exec.LookPath("llvm-cov"); err == nil {
|
||||||
|
llvmCov = path
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("failed to find turbo-cov or llvm-cov")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
r.covEnv = &cov.Env{
|
r.covEnv = &cov.Env{
|
||||||
LLVMBin: filepath.Dir(llvmCov),
|
Profdata: profdata,
|
||||||
Binary: dawnNode,
|
Binary: dawnNode,
|
||||||
|
Cov: llvmCov,
|
||||||
TurboCov: turboCov,
|
TurboCov: turboCov,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
|
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
|
||||||
#include "llvm/ProfileData/InstrProfReader.h"
|
#include "llvm/ProfileData/InstrProfReader.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include <fcntl.h> // _O_BINARY
|
||||||
|
#include <io.h> // _setmode
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -40,6 +45,13 @@ void emit(const llvm::StringRef& str) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, const char** argv) {
|
int main(int argc, const char** argv) {
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
// Change stdin & stdout from text mode to binary mode.
|
||||||
|
// This ensures sequences of \r\n are not changed to \n.
|
||||||
|
_setmode(_fileno(stdin), _O_BINARY);
|
||||||
|
_setmode(_fileno(stdout), _O_BINARY);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
fprintf(stderr, "turbo-cov <exe> <profdata>\n");
|
fprintf(stderr, "turbo-cov <exe> <profdata>\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -42,9 +42,10 @@ type Coverage struct {
|
||||||
|
|
||||||
// Env holds the environment settings for performing coverage processing.
|
// Env holds the environment settings for performing coverage processing.
|
||||||
type Env struct {
|
type Env struct {
|
||||||
LLVMBin string // path to the LLVM bin directory
|
Profdata string // path to the llvm-profdata tool
|
||||||
Binary string // path to the executable binary
|
Binary string // path to the executable binary
|
||||||
TurboCov string // path to turbo-cov (optional)
|
Cov string // path to the llvm-cov tool (one of Cov or TurboCov must be supplied)
|
||||||
|
TurboCov string // path to the turbo-cov tool (one of Cov or TurboCov must be supplied)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuntimeEnv returns the environment variable key=value pair for setting
|
// RuntimeEnv returns the environment variable key=value pair for setting
|
||||||
|
@ -92,33 +93,37 @@ func (e Env) AllSourceFiles() *Coverage {
|
||||||
// Import uses the llvm-profdata and llvm-cov tools to import the coverage
|
// Import uses the llvm-profdata and llvm-cov tools to import the coverage
|
||||||
// information from a .profraw file.
|
// information from a .profraw file.
|
||||||
func (e Env) Import(profrawPath string) (*Coverage, error) {
|
func (e Env) Import(profrawPath string) (*Coverage, error) {
|
||||||
llvmProfdataExe := filepath.Join(e.LLVMBin, "llvm-profdata"+fileutils.ExeExt)
|
|
||||||
llvmCovExe := filepath.Join(e.LLVMBin, "llvm-cov"+fileutils.ExeExt)
|
|
||||||
|
|
||||||
profdata := profrawPath + ".profdata"
|
profdata := profrawPath + ".profdata"
|
||||||
|
defer os.Remove(profdata)
|
||||||
|
|
||||||
if err := exec.Command(
|
if e.Profdata == "" {
|
||||||
llvmProfdataExe,
|
return nil, fmt.Errorf("cov.Env.Profdata must be specified")
|
||||||
|
}
|
||||||
|
if e.TurboCov == "" && e.Cov == "" {
|
||||||
|
return nil, fmt.Errorf("One of cov.Env.TurboCov or cov.Env.Cov must be specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
if out, err := exec.Command(
|
||||||
|
e.Profdata,
|
||||||
"merge",
|
"merge",
|
||||||
"-sparse",
|
"-sparse",
|
||||||
profrawPath,
|
profrawPath,
|
||||||
"-output",
|
"-output",
|
||||||
profdata).Run(); err != nil {
|
profdata).CombinedOutput(); err != nil {
|
||||||
return nil, fmt.Errorf("llvm-profdata errored: %w", err)
|
return nil, fmt.Errorf("llvm-profdata errored: %w\n%v", err, string(out))
|
||||||
}
|
}
|
||||||
defer os.Remove(profdata)
|
|
||||||
|
|
||||||
if e.TurboCov == "" {
|
if e.TurboCov == "" {
|
||||||
data, err := exec.Command(
|
data, err := exec.Command(
|
||||||
llvmCovExe,
|
e.Cov,
|
||||||
"export",
|
"export",
|
||||||
e.Binary,
|
e.Binary,
|
||||||
"-instr-profile="+profdata,
|
"-instr-profile="+profdata,
|
||||||
"-format=text",
|
"-format=text",
|
||||||
"-skip-expansions",
|
"-skip-expansions",
|
||||||
"-skip-functions").Output()
|
"-skip-functions").CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("llvm-cov errored: %v\n%v", string(err.(*exec.ExitError).Stderr), err)
|
return nil, fmt.Errorf("llvm-cov errored: %v\n%v", string(data), err)
|
||||||
}
|
}
|
||||||
cov, err := e.parseCov(data)
|
cov, err := e.parseCov(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -127,9 +132,9 @@ func (e Env) Import(profrawPath string) (*Coverage, error) {
|
||||||
return cov, nil
|
return cov, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := exec.Command(e.TurboCov, e.Binary, profdata).Output()
|
data, err := exec.Command(e.TurboCov, e.Binary, profdata).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("turbo-cov errored: %v\n%v", string(err.(*exec.ExitError).Stderr), err)
|
return nil, fmt.Errorf("turbo-cov errored: %v\n%v", string(data), err)
|
||||||
}
|
}
|
||||||
cov, err := e.parseTurboCov(data)
|
cov, err := e.parseTurboCov(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"compress/zlib"
|
"compress/zlib"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -156,7 +157,7 @@ func (t *Tree) writeFilesJSON(spansByID map[SpanID]Span, sb *strings.Builder) {
|
||||||
sb.WriteString(`,`)
|
sb.WriteString(`,`)
|
||||||
}
|
}
|
||||||
sb.WriteString(`"`)
|
sb.WriteString(`"`)
|
||||||
sb.WriteString(path)
|
sb.WriteString(filepath.ToSlash(path))
|
||||||
sb.WriteString(`":`)
|
sb.WriteString(`":`)
|
||||||
sb.WriteString(`{`)
|
sb.WriteString(`{`)
|
||||||
if totalLines := file.allSpans.NumLines(); totalLines > 0 {
|
if totalLines := file.allSpans.NumLines(); totalLines > 0 {
|
||||||
|
|
Loading…
Reference in New Issue