Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7cdf79c

Browse files
committed
Regres: Categorize crashes caused by debug macros
List most common failures on the daily report. Change-Id: Ia07f73601727f71e5f2abee7c40886e2a05209bb Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/27472 Reviewed-by: Nicolas Capens <[email protected]> Tested-by: Ben Clayton <[email protected]>
1 parent 306705c commit 7cdf79c

File tree

2 files changed

+178
-25
lines changed

2 files changed

+178
-25
lines changed

tests/regres/main.go

Lines changed: 156 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -387,16 +387,17 @@ func (r *regres) updateTestLists(client *gerrit.Client) error {
387387
test := r.newTest(headHash)
388388
defer test.cleanup()
389389

390+
// Always need to checkout the change.
391+
if err := test.checkout(); err != nil {
392+
return cause.Wrap(err, "Failed to checkout '%s'", headHash)
393+
}
394+
395+
// Load the test lists.
390396
testLists, err := test.loadTestLists(fullTestListRelPath)
391397
if err != nil {
392398
return cause.Wrap(err, "Failed to load full test lists for '%s'", headHash)
393399
}
394400

395-
// Couldn't load cached results. Have to build them.
396-
if err := test.checkout(); err != nil {
397-
return cause.Wrap(err, "Failed to checkout '%s'", headHash)
398-
}
399-
400401
// Build the change.
401402
if err := test.build(); err != nil {
402403
return cause.Wrap(err, "Failed to build '%s'", headHash)
@@ -421,23 +422,17 @@ func (r *regres) updateTestLists(client *gerrit.Client) error {
421422
}
422423

423424
log.Println("Checking for existing test list")
424-
changes, _, err := client.Changes.QueryChanges(&gerrit.QueryChangeOptions{
425-
QueryOptions: gerrit.QueryOptions{
426-
Query: []string{fmt.Sprintf(`status:open+owner:"%v"`, r.gerritEmail)},
427-
Limit: 1,
428-
},
429-
})
425+
existingChange, err := r.findTestListChange(client)
430426
if err != nil {
431-
return cause.Wrap(err, "Failed to checking for existing test list")
427+
return err
432428
}
433429

434430
commitMsg := strings.Builder{}
435431
commitMsg.WriteString(consts.TestListUpdateCommitSubjectPrefix + headHash.String()[:8])
436-
if results != nil && len(*changes) > 0 {
432+
if existingChange != nil {
437433
// Reuse gerrit change ID if there's already a change up for review.
438-
id := (*changes)[0].ChangeID
439434
commitMsg.WriteString("\n\n")
440-
commitMsg.WriteString("Change-Id: " + id)
435+
commitMsg.WriteString("Change-Id: " + existingChange.ChangeID + "\n")
441436
}
442437

443438
if err := git.Commit(test.srcDir, commitMsg.String(), git.CommitFlags{
@@ -460,9 +455,86 @@ func (r *regres) updateTestLists(client *gerrit.Client) error {
460455
log.Println("Test results posted for review")
461456
}
462457

458+
change, err := r.findTestListChange(client)
459+
if err != nil {
460+
return err
461+
}
462+
463+
if err := r.postMostCommonFailures(client, change, results); err != nil {
464+
return err
465+
}
466+
467+
return nil
468+
}
469+
470+
// postMostCommonFailures posts the most common failure cases as a review
471+
// comment on the given change.
472+
func (r *regres) postMostCommonFailures(client *gerrit.Client, change *gerrit.ChangeInfo, results *CommitTestResults) error {
473+
const limit = 25
474+
475+
failures := results.commonFailures()
476+
if len(failures) > limit {
477+
failures = failures[:limit]
478+
}
479+
sb := strings.Builder{}
480+
sb.WriteString(fmt.Sprintf("Top %v most common failures:\n", len(failures)))
481+
for _, f := range failures {
482+
lines := strings.Split(f.error, "\n")
483+
if len(lines) == 1 {
484+
line := lines[0]
485+
if line != "" {
486+
sb.WriteString(fmt.Sprintf(" %d occurrences: %v: %v\n", f.count, f.status, line))
487+
} else {
488+
sb.WriteString(fmt.Sprintf(" %d occurrences: %v\n", f.count, f.status))
489+
}
490+
} else {
491+
sb.WriteString(fmt.Sprintf(" %d occurrences: %v:\n", f.count, f.status))
492+
for _, l := range lines {
493+
sb.WriteString(" > ")
494+
sb.WriteString(l)
495+
sb.WriteString("\n")
496+
}
497+
}
498+
}
499+
msg := sb.String()
500+
501+
if r.dryRun {
502+
log.Printf("DRY RUN: add most common failures to '%v':\n%v\n", change.ChangeID, msg)
503+
} else {
504+
log.Printf("Posting most common failures to '%s'\n", change.ChangeID)
505+
_, _, err := client.Changes.SetReview(change.ChangeID, change.CurrentRevision, &gerrit.ReviewInput{
506+
Message: msg,
507+
Tag: "autogenerated:regress",
508+
})
509+
if err != nil {
510+
return cause.Wrap(err, "Failed to post comments on change '%s'", change.ChangeID)
511+
}
512+
}
463513
return nil
464514
}
465515

516+
func (r *regres) findTestListChange(client *gerrit.Client) (*gerrit.ChangeInfo, error) {
517+
log.Println("Checking for existing test list change")
518+
changes, _, err := client.Changes.QueryChanges(&gerrit.QueryChangeOptions{
519+
QueryOptions: gerrit.QueryOptions{
520+
Query: []string{fmt.Sprintf(`status:open+owner:"%v"`, r.gerritEmail)},
521+
Limit: 1,
522+
},
523+
ChangeOptions: gerrit.ChangeOptions{
524+
AdditionalFields: []string{"CURRENT_REVISION"},
525+
},
526+
})
527+
if err != nil {
528+
return nil, cause.Wrap(err, "Failed to checking for existing test list")
529+
}
530+
if len(*changes) > 0 {
531+
// TODO: This currently assumes that only change changes from
532+
// gerritEmail are test lists updates. This may not always be true.
533+
return &(*changes)[0], nil
534+
}
535+
return nil, nil
536+
}
537+
466538
// changeInfo holds the important information about a single, open change in
467539
// gerrit.
468540
type changeInfo struct {
@@ -827,6 +899,33 @@ func (r *CommitTestResults) save(path string) error {
827899
return nil
828900
}
829901

902+
type testStatusAndError struct {
903+
status testlist.Status
904+
error string
905+
}
906+
907+
type commonFailure struct {
908+
count int
909+
testStatusAndError
910+
}
911+
912+
func (r *CommitTestResults) commonFailures() []commonFailure {
913+
failures := map[testStatusAndError]int{}
914+
for _, test := range r.Tests {
915+
if !test.Status.Failing() {
916+
continue
917+
}
918+
key := testStatusAndError{test.Status, test.Err}
919+
failures[key] = failures[key] + 1
920+
}
921+
out := make([]commonFailure, 0, len(failures))
922+
for failure, count := range failures {
923+
out = append(out, commonFailure{count, failure})
924+
}
925+
sort.Slice(out, func(i, j int) bool { return out[i].count > out[j].count })
926+
return out
927+
}
928+
830929
// compare returns a string describing all differences between two
831930
// CommitTestResults. This string is used as the report message posted to the
832931
// gerrit code review.
@@ -903,6 +1002,10 @@ func compare(old, new *CommitTestResults) string {
9031002
{" Pass", testlist.Pass},
9041003
{" Fail", testlist.Fail},
9051004
{" Timeout", testlist.Timeout},
1005+
{" UNIMPLEMENTED()", testlist.Unimplemented},
1006+
{" UNREACHABLE()", testlist.Unreachable},
1007+
{" ASSERT()", testlist.Assert},
1008+
{" ABORT()", testlist.Abort},
9061009
{" Crash", testlist.Crash},
9071010
{" Not Supported", testlist.NotSupported},
9081011
{"Compatibility Warning", testlist.CompatibilityWarning},
@@ -978,15 +1081,26 @@ func (r TestResult) String() string {
9781081
return fmt.Sprintf("%s: %s", r.Test, r.Status)
9791082
}
9801083

981-
// Regular expression to parse the output of a dEQP test.
982-
var parseRE = regexp.MustCompile(`(Fail|Pass|NotSupported|CompatibilityWarning|QualityWarning) \(([\s\S]*)\)`)
1084+
var (
1085+
// Regular expression to parse the output of a dEQP test.
1086+
deqpRE = regexp.MustCompile(`(Fail|Pass|NotSupported|CompatibilityWarning|QualityWarning) \(([^\)]*)\)`)
1087+
// Regular expression to parse a test that failed due to UNIMPLEMENTED()
1088+
unimplementedRE = regexp.MustCompile(`[^\n]*UNIMPLEMENTED:[^\n]*`)
1089+
// Regular expression to parse a test that failed due to UNREACHABLE()
1090+
unreachableRE = regexp.MustCompile(`[^\n]*UNREACHABLE:[^\n]*`)
1091+
// Regular expression to parse a test that failed due to ASSERT()
1092+
assertRE = regexp.MustCompile(`[^\n]*ASSERT\([^\)]*\)[^\n]*`)
1093+
// Regular expression to parse a test that failed due to ABORT()
1094+
abortRE = regexp.MustCompile(`[^\n]*ABORT:[^\n]*`)
1095+
)
9831096

9841097
// deqpTestRoutine repeatedly runs the dEQP test executable exe with the tests
9851098
// taken from tests. The output of the dEQP test is parsed, and the test result
9861099
// is written to results.
9871100
// deqpTestRoutine only returns once the tests chan has been closed.
9881101
// deqpTestRoutine does not close the results chan.
9891102
func (t *test) deqpTestRoutine(exe string, tests <-chan string, results chan<- TestResult) {
1103+
nextTest:
9901104
for name := range tests {
9911105
// log.Printf("Running test '%s'\n", name)
9921106
env := []string{
@@ -996,24 +1110,43 @@ func (t *test) deqpTestRoutine(exe string, tests <-chan string, results chan<- T
9961110
"LIBC_FATAL_STDERR_=1", // Put libc explosions into logs.
9971111
}
9981112

999-
out, err := shell.Exec(testTimeout, exe, filepath.Dir(exe), env, "--deqp-surface-type=pbuffer", "-n="+name)
1113+
outRaw, err := shell.Exec(testTimeout, exe, filepath.Dir(exe), env, "--deqp-surface-type=pbuffer", "-n="+name)
1114+
out := string(outRaw)
1115+
out = strings.ReplaceAll(out, t.srcDir, "<SwiftShader>")
1116+
out = strings.ReplaceAll(out, exe, "<dEQP>")
10001117
switch err.(type) {
10011118
default:
1119+
for _, test := range []struct {
1120+
re *regexp.Regexp
1121+
s testlist.Status
1122+
}{
1123+
{unimplementedRE, testlist.Unimplemented},
1124+
{unreachableRE, testlist.Unreachable},
1125+
{assertRE, testlist.Assert},
1126+
{abortRE, testlist.Abort},
1127+
} {
1128+
if s := test.re.FindString(out); s != "" {
1129+
results <- TestResult{
1130+
Test: name,
1131+
Status: test.s,
1132+
Err: s,
1133+
}
1134+
continue nextTest
1135+
}
1136+
}
10021137
results <- TestResult{
10031138
Test: name,
10041139
Status: testlist.Crash,
1005-
Err: cause.Wrap(err, string(out)).Error(),
10061140
}
10071141
case shell.ErrTimeout:
10081142
results <- TestResult{
10091143
Test: name,
10101144
Status: testlist.Timeout,
1011-
Err: cause.Wrap(err, string(out)).Error(),
10121145
}
10131146
case nil:
1014-
toks := parseRE.FindStringSubmatch(string(out))
1147+
toks := deqpRE.FindStringSubmatch(out)
10151148
if len(toks) < 3 {
1016-
err := fmt.Sprintf("Couldn't parse test '%v' output:\n%s", name, string(out))
1149+
err := fmt.Sprintf("Couldn't parse test '%v' output:\n%s", name, out)
10171150
log.Println("Warning: ", err)
10181151
results <- TestResult{Test: name, Status: testlist.Fail, Err: err}
10191152
continue
@@ -1034,7 +1167,7 @@ func (t *test) deqpTestRoutine(exe string, tests <-chan string, results chan<- T
10341167
}
10351168
results <- TestResult{Test: name, Status: testlist.Fail, Err: err}
10361169
default:
1037-
err := fmt.Sprintf("Couldn't parse test output:\n%s", string(out))
1170+
err := fmt.Sprintf("Couldn't parse test output:\n%s", out)
10381171
log.Println("Warning: ", err)
10391172
results <- TestResult{Test: name, Status: testlist.Fail, Err: err}
10401173
}

tests/regres/testlist/testlist.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ const (
159159
Timeout = Status("TIMEOUT")
160160
// Crash is the status of a test that crashed.
161161
Crash = Status("CRASH")
162+
// Unimplemented is the status of a test that failed with UNIMPLEMENTED().
163+
Unimplemented = Status("UNIMPLEMENTED")
164+
// Unreachable is the status of a test that failed with UNREACHABLE().
165+
Unreachable = Status("UNREACHABLE")
166+
// Assert is the status of a test that failed with ASSERT() or ASSERT_MSG().
167+
Assert = Status("ASSERT")
168+
// Abort is the status of a test that failed with ABORT().
169+
Abort = Status("ABORT")
162170
// NotSupported is the status of a test feature not supported by the driver.
163171
NotSupported = Status("NOT_SUPPORTED")
164172
// CompatibilityWarning is the status passing test with a warning.
@@ -168,12 +176,24 @@ const (
168176
)
169177

170178
// Statuses is the full list of status types
171-
var Statuses = []Status{Pass, Fail, Timeout, Crash, NotSupported, CompatibilityWarning, QualityWarning}
179+
var Statuses = []Status{
180+
Pass,
181+
Fail,
182+
Timeout,
183+
Crash,
184+
Unimplemented,
185+
Unreachable,
186+
Assert,
187+
Abort,
188+
NotSupported,
189+
CompatibilityWarning,
190+
QualityWarning,
191+
}
172192

173193
// Failing returns true if the task status requires fixing.
174194
func (s Status) Failing() bool {
175195
switch s {
176-
case Fail, Timeout, Crash:
196+
case Fail, Timeout, Crash, Unimplemented, Unreachable, Assert:
177197
return true
178198
default:
179199
return false

0 commit comments

Comments
 (0)