Regres: Optimize coverage processing
This had regressed mostly due to a single thread processing a huge number of `Span.Add()`s.
Optimize for the common case, avoid `Span.Add()` in places where the data is already sorted, don't scan the entire file system for every test run.
Bug: b/152192800
Change-Id: Id183468263e65bfbcf387ec1c978d8b9de547cee
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43570
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/tests/regres/cov/import.go b/tests/regres/cov/import.go
index 1328b50..5240dd2 100644
--- a/tests/regres/cov/import.go
+++ b/tests/regres/cov/import.go
@@ -27,15 +27,6 @@
"../llvm"
)
-var ignorePaths = map[string]bool{
- "src/Common": true,
- "src/Main": true,
- "src/OpenGL": true,
- "src/Renderer": true,
- "src/Shader": true,
- "src/System": true,
-}
-
// File describes the coverage spans in a single source file.
type File struct {
Path string
@@ -63,6 +54,45 @@
return append(env, "LLVM_PROFILE_FILE="+coverageFile)
}
+// AllSourceFiles returns a *Coverage containing all the source files without
+// coverage data. This populates the coverage view with files even if they
+// didn't get compiled.
+func (e Env) AllSourceFiles() *Coverage {
+ var ignorePaths = map[string]bool{
+ "src/Common": true,
+ "src/Main": true,
+ "src/OpenGL": true,
+ "src/Renderer": true,
+ "src/Shader": true,
+ "src/System": true,
+ }
+
+ // Gather all the source files to include them even if there is no coverage
+ // information produced for these files. This highlights files that aren't
+ // even compiled.
+ cov := Coverage{}
+ allFiles := map[string]struct{}{}
+ filepath.Walk(filepath.Join(e.RootDir, "src"), func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ rel, err := filepath.Rel(e.RootDir, path)
+ if err != nil || ignorePaths[rel] {
+ return filepath.SkipDir
+ }
+ if !info.IsDir() {
+ switch filepath.Ext(path) {
+ case ".h", ".c", ".cc", ".cpp", ".hpp":
+ if _, seen := allFiles[rel]; !seen {
+ cov.Files = append(cov.Files, File{Path: rel})
+ }
+ }
+ }
+ return nil
+ })
+ return &cov
+}
+
// Import uses the llvm-profdata and llvm-cov tools to import the coverage
// information from a .profraw file.
func (e Env) Import(profrawPath string) (*Coverage, error) {
@@ -108,32 +138,6 @@
return nil, cause.Wrap(err, "Couldn't process turbo-cov output")
}
- // Gather all the source files to include them even if there is no coverage
- // information produced for these files. This highlights files that aren't
- // even compiled.
- allFiles := map[string]struct{}{}
- for _, file := range cov.Files {
- allFiles[file.Path] = struct{}{}
- }
- filepath.Walk(filepath.Join(e.RootDir, "src"), func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- rel, err := filepath.Rel(e.RootDir, path)
- if err != nil || ignorePaths[rel] {
- return filepath.SkipDir
- }
- if !info.IsDir() {
- switch filepath.Ext(path) {
- case ".h", ".c", ".cc", ".cpp", ".hpp":
- if _, seen := allFiles[rel]; !seen {
- cov.Files = append(cov.Files, File{Path: rel})
- }
- }
- }
- return nil
- })
-
return cov, nil
}
@@ -185,9 +189,9 @@
start := Location{(int)(f.Segments[sIdx][0].(float64)), (int)(f.Segments[sIdx][1].(float64))}
end := Location{(int)(f.Segments[sIdx+1][0].(float64)), (int)(f.Segments[sIdx+1][1].(float64))}
if covered := f.Segments[sIdx][2].(float64) != 0; covered {
- file.Covered.Add(Span{start, end})
+ file.Covered = appendSpan(file.Covered, Span{start, end})
} else {
- file.Uncovered.Add(Span{start, end})
+ file.Uncovered = appendSpan(file.Uncovered, Span{start, end})
}
}
if len(file.Covered) > 0 {
@@ -251,9 +255,9 @@
end := segments[sIdx+1].location
if segments[sIdx].covered {
if segments[sIdx].count > 0 {
- file.Covered.Add(Span{start, end})
+ file.Covered = appendSpan(file.Covered, Span{start, end})
} else {
- file.Uncovered.Add(Span{start, end})
+ file.Uncovered = appendSpan(file.Uncovered, Span{start, end})
}
}
}
diff --git a/tests/regres/cov/span.go b/tests/regres/cov/span.go
index 9545936..36a11c4 100644
--- a/tests/regres/cov/span.go
+++ b/tests/regres/cov/span.go
@@ -76,6 +76,9 @@
// Before returns true if span s comes before o.
func (s Span) Before(o Span) bool { return s.Compare(o) == -1 }
+// Inside returns true if span s fits entirely inside o.
+func (s Span) Inside(o Span) bool { return s.Start.Compare(o.Start) >= 0 && s.End.Compare(o.End) <= 0 }
+
// SpanList is a sorted list of spans. Use SpanList.Add() to insert new spans.
type SpanList []Span
@@ -87,6 +90,11 @@
// [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 | idxEnd: 2
// [0] [1] [2] [3] [4] | idxStart: 2 | idxEnd: 2
idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) >= 0 })
+
+ if idxStart < len(*l) && s.Inside((*l)[idxStart]) {
+ return // No change.
+ }
+
idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) > 0 })
if idxStart < idxEnd {
diff --git a/tests/regres/deqp/deqp.go b/tests/regres/deqp/deqp.go
index e44be99..33573bf 100644
--- a/tests/regres/deqp/deqp.go
+++ b/tests/regres/deqp/deqp.go
@@ -216,6 +216,7 @@
if c.CoverageEnv != nil {
out.Coverage = &cov.Tree{}
+ out.Coverage.Add(cov.Path{}, c.CoverageEnv.AllSourceFiles())
}
// Collect the results.