diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2021-01-17 12:25:56 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2021-01-17 15:44:44 +0100 |
| commit | fbc119e631b5fe4eae43b1a37c660038ac3788cd (patch) | |
| tree | 219f7c279261e624385ab3e2946ff76675553833 /pkg | |
| parent | 813be5426a31b5b3ead90cf5729c8b7a7a17d7c1 (diff) | |
pkg/report: support alternative bug titles
Update #1575
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/report/freebsd.go | 3 | ||||
| -rw-r--r-- | pkg/report/linux.go | 5 | ||||
| -rw-r--r-- | pkg/report/report.go | 18 | ||||
| -rw-r--r-- | pkg/report/report_test.go | 36 |
4 files changed, 49 insertions, 13 deletions
diff --git a/pkg/report/freebsd.go b/pkg/report/freebsd.go index e5a1fc92e..3bf0b8eb4 100644 --- a/pkg/report/freebsd.go +++ b/pkg/report/freebsd.go @@ -56,8 +56,9 @@ func (ctx *freebsd) Parse(output []byte) *Report { if oops == nil { return nil } - title, corrupted, _ := extractDescription(output[rep.StartPos:], oops, freebsdStackParams) + title, corrupted, altTitles, _ := extractDescription(output[rep.StartPos:], oops, freebsdStackParams) rep.Title = title + rep.AltTitles = altTitles rep.Corrupted = corrupted != "" rep.CorruptedReason = corrupted return rep diff --git a/pkg/report/linux.go b/pkg/report/linux.go index 8aed7d4f4..dd781ae89 100644 --- a/pkg/report/linux.go +++ b/pkg/report/linux.go @@ -145,17 +145,18 @@ func (ctx *linux) Parse(output []byte) *Report { } endPos, reportEnd, report, prefix := ctx.findReport(output, oops, startPos, context, questionable) rep.EndPos = endPos - title, corrupted, format := extractDescription(report[:reportEnd], oops, linuxStackParams) + title, corrupted, altTitles, format := extractDescription(report[:reportEnd], oops, linuxStackParams) if title == "" { prefix = nil report = output[rep.StartPos:rep.EndPos] - title, corrupted, format = extractDescription(report, oops, linuxStackParams) + title, corrupted, altTitles, format = extractDescription(report, oops, linuxStackParams) if title == "" { panic(fmt.Sprintf("non matching oops for %q context=%q in:\n%s\n", oops.header, context, report)) } } rep.Title = title + rep.AltTitles = altTitles rep.Corrupted = corrupted != "" rep.CorruptedReason = corrupted for _, line := range prefix { diff --git a/pkg/report/report.go b/pkg/report/report.go index 397684569..72d52bbf0 100644 --- a/pkg/report/report.go +++ b/pkg/report/report.go @@ -32,6 +32,9 @@ type Reporter interface { type Report struct { // Title contains a representative description of the first oops. Title string + // Alternative titles, used for better deduplication. + // If two crashes have a non-empty intersection of Title/AltTitles, they are considered the same bug. + AltTitles []string // Bug type (e.g. hang, memory leak, etc). Type Type // The indicative function name. @@ -170,6 +173,9 @@ func (wrap *reporterWrapper) Parse(output []byte) *Report { return nil } rep.Title = sanitizeTitle(replaceTable(dynamicTitleReplacement, rep.Title)) + for i, title := range rep.AltTitles { + rep.AltTitles[i] = sanitizeTitle(replaceTable(dynamicTitleReplacement, title)) + } rep.Suppressed = matchesAny(rep.Output, wrap.suppressions) if bytes.Contains(rep.Output, gceConsoleHangup) { rep.Corrupted = true @@ -329,6 +335,9 @@ type oopsFormat struct { // Strings captured by title (or by report if present) are passed as input. // If stack is not nil, extracted function name is passed as an additional last argument. fmt string + // Alternative titles used for better crash deduplication. + // Format is the same as for fmt. + alt []string // If not nil, a function name is extracted from the report and passed to fmt. // If not nil but frame extraction fails, the report is considered corrupted. stack *stackFmt @@ -398,7 +407,8 @@ func matchOops(line []byte, oops *oops, ignores []*regexp.Regexp) bool { return true } -func extractDescription(output []byte, oops *oops, params *stackParams) (desc, corrupted string, format oopsFormat) { +func extractDescription(output []byte, oops *oops, params *stackParams) ( + desc, corrupted string, altTitles []string, format oopsFormat) { startPos := len(output) matchedTitle := false for _, f := range oops.formats { @@ -438,6 +448,9 @@ func extractDescription(output []byte, oops *oops, params *stackParams) (desc, c args = append(args, frame) } desc = fmt.Sprintf(f.fmt, args...) + for _, alt := range f.alt { + altTitles = append(altTitles, fmt.Sprintf(alt, args...)) + } format = f } if desc == "" { @@ -591,8 +604,9 @@ func simpleLineParser(output []byte, oopses []*oops, params *stackParams, ignore if oops == nil { return nil } - title, corrupted, _ := extractDescription(output[rep.StartPos:], oops, params) + title, corrupted, altTitles, _ := extractDescription(output[rep.StartPos:], oops, params) rep.Title = title + rep.AltTitles = altTitles rep.Report = output[rep.StartPos:] rep.Corrupted = corrupted != "" rep.CorruptedReason = corrupted diff --git a/pkg/report/report_test.go b/pkg/report/report_test.go index 05883a21f..355edc764 100644 --- a/pkg/report/report_test.go +++ b/pkg/report/report_test.go @@ -10,7 +10,9 @@ import ( "fmt" "io/ioutil" "path/filepath" + "reflect" "regexp" + "sort" "strings" "testing" @@ -29,6 +31,7 @@ type ParseTest struct { FileName string Log []byte Title string + AltTitles []string Type Type Frame string StartLine string @@ -92,6 +95,7 @@ func testParseFile(t *testing.T, reporter Reporter, fn string) { func parseHeaderLine(t *testing.T, test *ParseTest, ln string) { const ( titlePrefix = "TITLE: " + altTitlePrefix = "ALT: " typePrefix = "TYPE: " framePrefix = "FRAME: " startPrefix = "START: " @@ -103,6 +107,8 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) { case strings.HasPrefix(ln, "#"): case strings.HasPrefix(ln, titlePrefix): test.Title = ln[len(titlePrefix):] + case strings.HasPrefix(ln, altTitlePrefix): + test.AltTitles = append(test.AltTitles, ln[len(altTitlePrefix):]) case strings.HasPrefix(ln, typePrefix): switch v := ln[len(typePrefix):]; v { case Hang.String(): @@ -159,23 +165,34 @@ func testParseImpl(t *testing.T, reporter Reporter, test *ParseTest) { t.Fatalf("found crash, but title is empty") } title, corrupted, corruptedReason, suppressed, typ, frame := "", false, "", false, Unknown, "" + var altTitles []string if rep != nil { title = rep.Title + altTitles = rep.AltTitles corrupted = rep.Corrupted corruptedReason = rep.CorruptedReason suppressed = rep.Suppressed typ = rep.Type frame = rep.Frame } - if title != test.Title || corrupted != test.Corrupted || suppressed != test.Suppressed || - typ != test.Type || test.Frame != "" && frame != test.Frame { + sort.Strings(altTitles) + sort.Strings(test.AltTitles) + if title != test.Title || !reflect.DeepEqual(altTitles, test.AltTitles) || corrupted != test.Corrupted || + suppressed != test.Suppressed || typ != test.Type || test.Frame != "" && frame != test.Frame { if *flagUpdate && test.StartLine+test.EndLine == "" { - updateReportTest(t, test, title, corrupted, suppressed, typ, frame) + updateReportTest(t, test, title, altTitles, corrupted, suppressed, typ, frame) } - t.Fatalf("want:\nTITLE: %s\nTYPE: %v\nFRAME: %v\nCORRUPTED: %v\nSUPPRESSED: %v\n"+ - "got:\nTITLE: %s\nTYPE: %v\nFRAME: %v\nCORRUPTED: %v (%v)\nSUPPRESSED: %v\n", - test.Title, test.Type, test.Frame, test.Corrupted, test.Suppressed, - title, typ, frame, corrupted, corruptedReason, suppressed) + gotAltTitles, wantAltTitles := "", "" + for _, t := range altTitles { + gotAltTitles += "ALT: " + t + "\n" + } + for _, t := range test.AltTitles { + wantAltTitles += "ALT: " + t + "\n" + } + t.Fatalf("want:\nTITLE: %s\n%sTYPE: %v\nFRAME: %v\nCORRUPTED: %v\nSUPPRESSED: %v\n"+ + "got:\nTITLE: %s\n%sTYPE: %v\nFRAME: %v\nCORRUPTED: %v (%v)\nSUPPRESSED: %v\n", + test.Title, wantAltTitles, test.Type, test.Frame, test.Corrupted, test.Suppressed, + title, gotAltTitles, typ, frame, corrupted, corruptedReason, suppressed) } if title != "" && len(rep.Report) == 0 { t.Fatalf("found crash message but report is empty") @@ -228,10 +245,13 @@ func checkReport(t *testing.T, reporter Reporter, rep *Report, test *ParseTest) } } -func updateReportTest(t *testing.T, test *ParseTest, title string, corrupted, suppressed bool, +func updateReportTest(t *testing.T, test *ParseTest, title string, altTitles []string, corrupted, suppressed bool, typ Type, frame string) { buf := new(bytes.Buffer) fmt.Fprintf(buf, "TITLE: %v\n", title) + for _, t := range altTitles { + fmt.Fprintf(buf, "ALT: %v\n", t) + } if typ != Unknown { fmt.Fprintf(buf, "TYPE: %v\n", typ) } |
