Commit 536e2c80 by Ben Clayton

Regres: Generate coverage data on nightly runs

Push this to https://github.com/swiftshader-regres/swiftshader-coverage so that it can be viewed at: https://swiftshader-regres.github.io/swiftshader-coverage/ There's quite a lot of new code and fixes in this change. The most notable: * The regres daily run for the subzero backend now produces combined coverage information for all the test runs. The LLVM backend does not produce coverage information. * Regres now takes two additional command line arguments: `gh-user` and `gh-pass` for the swiftshader-regres account. If you omit these, then coverage will not be produced. * test.srcDir has been renamed to checkoutDir, as this was confusing with the `src` directory in the repo. * The coverage JSON now contains a root field to describe the git revision to which it relates. This prevents the coverage going out of sync with the source. * `git.CheckoutRemoteBranch()` drops back to a depth of 1 again. This was only increased to 99 to deal with issues checking out from gitlab, which we don't do any more. * Regres now builds using `third_party/llvm-10.0` * Fixed the `--limit` regres command line flag which wasn't actually limiting, as it was using the len() on the number of groups, not the number of tests. Bug: b/152192800 Bug: b/152339534 Change-Id: I2d25735f485097d4efb080546d989056a3a8aab3 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43168 Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com>
parent c346653b
...@@ -132,7 +132,7 @@ func run() error { ...@@ -132,7 +132,7 @@ func run() error {
} }
if *genCoverage { if *genCoverage {
if err := ioutil.WriteFile("coverage.json", []byte(res.Coverage.JSON()), 0666); err != nil { if err := ioutil.WriteFile("coverage.json", []byte(res.Coverage.JSON("master")), 0666); err != nil {
return err return err
} }
} }
......
...@@ -85,24 +85,24 @@ func (e Env) Import(profrawPath string) (*Coverage, error) { ...@@ -85,24 +85,24 @@ func (e Env) Import(profrawPath string) (*Coverage, error) {
} }
defer os.Remove(profdata) defer os.Remove(profdata)
args := []string{
"export",
e.ExePath,
"-instr-profile=" + profdata,
"-format=text",
}
if e.LLVM.Version.GreaterEqual(llvm.Version{Major: 9}) {
// LLVM 9 has new flags that omit stuff we don't care about.
args = append(args,
"-skip-expansions",
"-skip-functions",
)
}
if e.TurboCov == "" { if e.TurboCov == "" {
args := []string{
"export",
e.ExePath,
"-instr-profile=" + profdata,
"-format=text",
}
if e.LLVM.Version.GreaterEqual(llvm.Version{Major: 9}) {
// LLVM 9 has new flags that omit stuff we don't care about.
args = append(args,
"-skip-expansions",
"-skip-functions",
)
}
data, err := exec.Command(e.LLVM.Cov(), args...).Output() data, err := exec.Command(e.LLVM.Cov(), args...).Output()
if err != nil { if err != nil {
return nil, cause.Wrap(err, "llvm-cov errored: %v", string(data)) return nil, cause.Wrap(err, "llvm-cov errored: %v", string(err.(*exec.ExitError).Stderr))
} }
cov, err := e.parseCov(data) cov, err := e.parseCov(data)
if err != nil { if err != nil {
...@@ -113,7 +113,7 @@ func (e Env) Import(profrawPath string) (*Coverage, error) { ...@@ -113,7 +113,7 @@ func (e Env) Import(profrawPath string) (*Coverage, error) {
data, err := exec.Command(e.TurboCov, e.ExePath, profdata).Output() data, err := exec.Command(e.TurboCov, e.ExePath, profdata).Output()
if err != nil { if err != nil {
return nil, cause.Wrap(err, "turbo-cov errored: %v", string(data)) return nil, cause.Wrap(err, "turbo-cov errored: %v", string(err.(*exec.ExitError).Stderr))
} }
cov, err := e.parseTurboCov(data) cov, err := e.parseTurboCov(data)
if err != nil { if err != nil {
...@@ -566,12 +566,15 @@ func indent(s string) string { ...@@ -566,12 +566,15 @@ func indent(s string) string {
} }
// JSON returns the full test tree serialized to JSON. // JSON returns the full test tree serialized to JSON.
func (t *Tree) JSON() string { func (t *Tree) JSON(revision string) string {
sb := &strings.Builder{} sb := &strings.Builder{}
sb.WriteString(`{`) sb.WriteString(`{`)
// write the revision
sb.WriteString(`"r":"` + revision + `"`)
// write the strings // write the strings
sb.WriteString(`"n":[`) sb.WriteString(`,"n":[`)
for i, s := range t.strings.s { for i, s := range t.strings.s {
if i > 0 { if i > 0 {
sb.WriteString(`,`) sb.WriteString(`,`)
......
...@@ -47,7 +47,7 @@ int main(int argc, const char **argv) ...@@ -47,7 +47,7 @@ int main(int argc, const char **argv)
{ {
if(argc < 3) if(argc < 3)
{ {
fprintf(stderr, "llvm-cov-bin <exe> <profdata>\n"); fprintf(stderr, "turbo-cov <exe> <profdata>\n");
return 1; return 1;
} }
......
...@@ -158,7 +158,8 @@ func (c *Config) Run() (*Results, error) { ...@@ -158,7 +158,8 @@ func (c *Config) Run() (*Results, error) {
// For each API that we are testing // For each API that we are testing
for _, list := range c.TestLists { for _, list := range c.TestLists {
// Resolve the test runner // Resolve the test runner
var exe string exe, supportsCoverage := "", false
switch list.API { switch list.API {
case testlist.EGL: case testlist.EGL:
exe = c.ExeEgl exe = c.ExeEgl
...@@ -167,7 +168,7 @@ func (c *Config) Run() (*Results, error) { ...@@ -167,7 +168,7 @@ func (c *Config) Run() (*Results, error) {
case testlist.GLES3: case testlist.GLES3:
exe = c.ExeGles3 exe = c.ExeGles3
case testlist.Vulkan: case testlist.Vulkan:
exe = c.ExeVulkan exe, supportsCoverage = c.ExeVulkan, true
default: default:
return nil, fmt.Errorf("Unknown API '%v'", list.API) return nil, fmt.Errorf("Unknown API '%v'", list.API)
} }
...@@ -182,7 +183,7 @@ func (c *Config) Run() (*Results, error) { ...@@ -182,7 +183,7 @@ func (c *Config) Run() (*Results, error) {
wg.Add(c.NumParallelTests) wg.Add(c.NumParallelTests)
for i := 0; i < c.NumParallelTests; i++ { for i := 0; i < c.NumParallelTests; i++ {
go func(index int) { go func(index int) {
c.TestRoutine(exe, tests, results, index) c.TestRoutine(exe, tests, results, index, supportsCoverage)
wg.Done() wg.Done()
}(goroutineIndex) }(goroutineIndex)
goroutineIndex++ goroutineIndex++
...@@ -255,7 +256,7 @@ func (c *Config) Run() (*Results, error) { ...@@ -255,7 +256,7 @@ func (c *Config) Run() (*Results, error) {
// is written to results. // is written to results.
// TestRoutine only returns once the tests chan has been closed. // TestRoutine only returns once the tests chan has been closed.
// TestRoutine does not close the results chan. // TestRoutine does not close the results chan.
func (c *Config) TestRoutine(exe string, tests <-chan string, results chan<- TestResult, goroutineIndex int) { func (c *Config) TestRoutine(exe string, tests <-chan string, results chan<- TestResult, goroutineIndex int, supportsCoverage bool) {
// Context for the GCOV_PREFIX environment variable: // Context for the GCOV_PREFIX environment variable:
// If you compile SwiftShader with gcc and the --coverage flag, the build will contain coverage instrumentation. // If you compile SwiftShader with gcc and the --coverage flag, the build will contain coverage instrumentation.
// We can use this to get the code coverage of SwiftShader from running dEQP. // We can use this to get the code coverage of SwiftShader from running dEQP.
...@@ -286,8 +287,10 @@ func (c *Config) TestRoutine(exe string, tests <-chan string, results chan<- Tes ...@@ -286,8 +287,10 @@ func (c *Config) TestRoutine(exe string, tests <-chan string, results chan<- Tes
} }
coverageFile := filepath.Join(c.TempDir, fmt.Sprintf("%v.profraw", goroutineIndex)) coverageFile := filepath.Join(c.TempDir, fmt.Sprintf("%v.profraw", goroutineIndex))
if c.CoverageEnv != nil { if supportsCoverage {
env = cov.AppendRuntimeEnv(env, coverageFile) if c.CoverageEnv != nil {
env = cov.AppendRuntimeEnv(env, coverageFile)
}
} }
logPath := filepath.Join(c.TempDir, fmt.Sprintf("%v.log", goroutineIndex)) logPath := filepath.Join(c.TempDir, fmt.Sprintf("%v.log", goroutineIndex))
...@@ -312,10 +315,10 @@ nextTest: ...@@ -312,10 +315,10 @@ nextTest:
} }
var coverage *cov.Coverage var coverage *cov.Coverage
if c.CoverageEnv != nil { if c.CoverageEnv != nil && supportsCoverage { // IsFile() check here is for GLES tests that don't emit coverage.
coverage, err = c.CoverageEnv.Import(coverageFile) coverage, err = c.CoverageEnv.Import(coverageFile)
if err != nil { if err != nil {
log.Printf("Warning: Failed to get test coverage for test '%v'. %v", name, err) log.Printf("Warning: Failed to process test coverage for test '%v'. %v", name, err)
} }
os.Remove(coverageFile) os.Remove(coverageFile)
} }
......
...@@ -120,10 +120,8 @@ func CheckoutRemoteBranch(path, url string, branch string) error { ...@@ -120,10 +120,8 @@ func CheckoutRemoteBranch(path, url string, branch string) error {
for _, cmds := range [][]string{ for _, cmds := range [][]string{
{"init"}, {"init"},
{"remote", "add", "origin", url}, {"remote", "add", "origin", url},
// Note: this depth is here to prevent massive dEQP checkouts that can {"fetch", "origin", "--depth=1", branch},
// take all day. If the commit cannot be found in the checked out branch {"checkout", branch},
// then this limit may need to be increased.
{"fetch", "origin", "--depth=99", branch},
} { } {
if err := shell.Shell(gitTimeout, exe, path, cmds...); err != nil { if err := shell.Shell(gitTimeout, exe, path, cmds...); err != nil {
os.RemoveAll(path) os.RemoveAll(path)
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
go run $ROOT_DIR/cmd/regres/main.go $@ go run $ROOT_DIR/cmd/regres/main.go $@ 2>&1 | tee regres-log.txt
...@@ -79,6 +79,20 @@ func (g Group) Filter(pred func(string) bool) Group { ...@@ -79,6 +79,20 @@ func (g Group) Filter(pred func(string) bool) Group {
return out return out
} }
// Limit returns a new Group that contains a maximum of limit tests.
func (g Group) Limit(limit int) Group {
out := Group{
Name: g.Name,
File: g.File,
API: g.API,
Tests: g.Tests,
}
if len(g.Tests) > limit {
out.Tests = g.Tests[:limit]
}
return out
}
// Lists is the full list of tests to be run. // Lists is the full list of tests to be run.
type Lists []Group type Lists []Group
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment