Skip to content

Commit a6847d2

Browse files
committed
dashboard/app: integrate tail reports
1. add bug consequences stat tooltip to the Rank column 2. add tail titles and tail report to the bug page
1 parent d401b9d commit a6847d2

File tree

14 files changed

+271
-183
lines changed

14 files changed

+271
-183
lines changed

dashboard/app/api.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,9 @@ func reportCrash(c context.Context, build *Build, req *dashapi.Crash) (*Bug, err
916916
bug.SetAutoSubsystems(c, newSubsystems, now, subsystemService.Revision)
917917
}
918918
bug.increaseCrashStats(now)
919+
if err = bug.addTitleStat(req.TailTitles); err != nil {
920+
return fmt.Errorf("failed to add title stat: %w", err)
921+
}
919922
bug.HappenedOn = mergeString(bug.HappenedOn, build.Manager)
920923
// Migration of older entities (for new bugs Title is always in MergedTitles).
921924
bug.MergedTitles = mergeString(bug.MergedTitles, bug.Title)
@@ -975,10 +978,11 @@ func (crash *Crash) UpdateReportingPriority(c context.Context, build *Build, bug
975978
func saveCrash(c context.Context, ns string, req *dashapi.Crash, bug *Bug, bugKey *db.Key,
976979
build *Build, assets []Asset) error {
977980
crash := &Crash{
978-
Title: req.Title,
979-
Manager: build.Manager,
980-
BuildID: req.BuildID,
981-
Time: timeNow(c),
981+
Title: req.Title,
982+
TailTitles: req.TailTitles,
983+
Manager: build.Manager,
984+
BuildID: req.BuildID,
985+
Time: timeNow(c),
982986
Maintainers: email.MergeEmailLists(req.Maintainers,
983987
GetEmails(req.Recipients, dashapi.To),
984988
GetEmails(req.Recipients, dashapi.Cc)),
@@ -996,6 +1000,13 @@ func saveCrash(c context.Context, ns string, req *dashapi.Crash, bug *Bug, bugKe
9961000
if crash.Report, err = putText(c, ns, textCrashReport, req.Report); err != nil {
9971001
return err
9981002
}
1003+
for _, tailReport := range req.TailReports {
1004+
tailReportID, err := putText(c, ns, textCrashReport, tailReport)
1005+
if err != nil {
1006+
return err
1007+
}
1008+
crash.TailReports = append(crash.TailReports, tailReportID)
1009+
}
9991010
if crash.ReproSyz, err = putText(c, ns, textReproSyz, req.ReproSyz); err != nil {
10001011
return err
10011012
}
@@ -1073,6 +1084,11 @@ func purgeOldCrashes(c context.Context, bug *Bug, bugKey *db.Key) {
10731084
if crash.Report != 0 {
10741085
toDelete = append(toDelete, db.NewKey(c, textCrashReport, "", crash.Report, nil))
10751086
}
1087+
if len(crash.TailReports) != 0 {
1088+
for _, tailReport := range crash.TailReports {
1089+
toDelete = append(toDelete, db.NewKey(c, textCrashReport, "", tailReport, nil))
1090+
}
1091+
}
10761092
if crash.ReproSyz != 0 {
10771093
toDelete = append(toDelete, db.NewKey(c, textReproSyz, "", crash.ReproSyz, nil))
10781094
}

dashboard/app/entities_datastore.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/google/syzkaller/dashboard/dashapi"
1515
"github.com/google/syzkaller/pkg/hash"
16+
"github.com/google/syzkaller/pkg/report"
1617
"github.com/google/syzkaller/pkg/subsystem"
1718
db "google.golang.org/appengine/v2/datastore"
1819
)
@@ -91,6 +92,7 @@ type Bug struct {
9192
Title string
9293
MergedTitles []string // crash titles that we already merged into this bug
9394
AltTitles []string // alternative crash titles that we may merge into this bug
95+
TitleStat string // serialized report.TitleStat
9496
Status int
9597
StatusReason dashapi.BugStatusReason // e.g. if the bug status is "invalid", here's the reason why
9698
DupOf string
@@ -348,6 +350,7 @@ type Crash struct {
348350
// May be different from bug.Title due to AltTitles.
349351
// May be empty for old bugs, in such case bug.Title is the right title.
350352
Title string
353+
TailTitles []string
351354
Manager string
352355
BuildID string
353356
Time time.Time
@@ -357,6 +360,7 @@ type Crash struct {
357360
Log int64 // reference to CrashLog text entity
358361
Flags int64 // properties of the Crash
359362
Report int64 // reference to CrashReport text entity
363+
TailReports []int64 // references to CrashReport text entity
360364
ReportElements CrashReportElements // parsed parts of the crash report
361365
ReproOpts []byte `datastore:",noindex"`
362366
ReproSyz int64 // reference to ReproSyz text entity
@@ -985,6 +989,20 @@ func (bug *Bug) increaseCrashStats(now time.Time) {
985989
}
986990
}
987991

992+
func (bug *Bug) addTitleStat(titles []string) error {
993+
ts, err := report.TitleStatFromBytes([]byte(bug.TitleStat))
994+
if err != nil {
995+
return err
996+
}
997+
ts.Add(titles)
998+
bytes, err := ts.ToBytes()
999+
if err != nil {
1000+
return err
1001+
}
1002+
bug.TitleStat = string(bytes)
1003+
return nil
1004+
}
1005+
9881006
func (bug *Bug) dailyStatsTail(from time.Time) []BugDailyStats {
9891007
startDate := timeDate(from)
9901008
startPos := len(bug.DailyStats)

dashboard/app/main.go

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ type uiBug struct {
368368
Namespace string
369369
Title string
370370
ImpactScore int
371+
RankTooltip string
371372
NumCrashes int64
372373
NumCrashesBad bool
373374
BisectCause BisectStatus
@@ -398,19 +399,21 @@ type uiBugLabel struct {
398399
}
399400

400401
type uiCrash struct {
401-
Title string
402-
Manager string
403-
Time time.Time
404-
Maintainers string
405-
LogLink string
406-
LogHasStrace bool
407-
ReportLink string
408-
ReproSyzLink string
409-
ReproCLink string
410-
ReproIsRevoked bool
411-
ReproLogLink string
412-
MachineInfoLink string
413-
Assets []*uiAsset
402+
Title string
403+
TailTitles []string
404+
Manager string
405+
Time time.Time
406+
Maintainers string
407+
LogLink string
408+
LogHasStrace bool
409+
ReportLink string
410+
TailReportsLinks []string
411+
ReproSyzLink string
412+
ReproCLink string
413+
ReproIsRevoked bool
414+
ReproLogLink string
415+
MachineInfoLink string
416+
Assets []*uiAsset
414417
*uiBuild
415418
}
416419

@@ -1938,10 +1941,16 @@ func createUIBug(c context.Context, bug *Bug, state *ReportingState, managers []
19381941
log.Errorf(c, "failed to generate credit email: %v", err)
19391942
}
19401943
}
1944+
1945+
titleStat, err := report.TitleStatFromBytes([]byte(bug.TitleStat))
1946+
if err != nil {
1947+
log.Errorf(c, "report.TitleStatFromBytes: %v", err)
1948+
}
19411949
uiBug := &uiBug{
19421950
Namespace: bug.Namespace,
19431951
Title: bug.displayTitle(),
19441952
ImpactScore: report.TitlesToImpact(bug.Title, bug.AltTitles...),
1953+
RankTooltip: report.HigherRankTooltip(bug.Title, titleStat.Explain()),
19451954
BisectCause: bug.BisectCause,
19461955
BisectFix: bug.BisectFix,
19471956
NumCrashes: bug.NumCrashes,
@@ -2076,20 +2085,26 @@ func makeUIAssets(c context.Context, build *Build, crash *Crash, forReport bool)
20762085
}
20772086

20782087
func makeUICrash(c context.Context, crash *Crash, build *Build) *uiCrash {
2088+
var tailReportsLinks []string
2089+
for _, tailReportID := range crash.TailReports {
2090+
tailReportsLinks = append(tailReportsLinks, textLink(textCrashReport, tailReportID))
2091+
}
20792092
ui := &uiCrash{
2080-
Title: crash.Title,
2081-
Manager: crash.Manager,
2082-
Time: crash.Time,
2083-
Maintainers: strings.Join(crash.Maintainers, ", "),
2084-
LogLink: textLink(textCrashLog, crash.Log),
2085-
LogHasStrace: dashapi.CrashFlags(crash.Flags)&dashapi.CrashUnderStrace > 0,
2086-
ReportLink: textLink(textCrashReport, crash.Report),
2087-
ReproSyzLink: textLink(textReproSyz, crash.ReproSyz),
2088-
ReproCLink: textLink(textReproC, crash.ReproC),
2089-
ReproLogLink: textLink(textReproLog, crash.ReproLog),
2090-
ReproIsRevoked: crash.ReproIsRevoked,
2091-
MachineInfoLink: textLink(textMachineInfo, crash.MachineInfo),
2092-
Assets: makeUIAssets(c, build, crash, true),
2093+
Title: crash.Title,
2094+
TailTitles: crash.TailTitles,
2095+
Manager: crash.Manager,
2096+
Time: crash.Time,
2097+
Maintainers: strings.Join(crash.Maintainers, ", "),
2098+
LogLink: textLink(textCrashLog, crash.Log),
2099+
LogHasStrace: dashapi.CrashFlags(crash.Flags)&dashapi.CrashUnderStrace > 0,
2100+
ReportLink: textLink(textCrashReport, crash.Report),
2101+
TailReportsLinks: tailReportsLinks,
2102+
ReproSyzLink: textLink(textReproSyz, crash.ReproSyz),
2103+
ReproCLink: textLink(textReproC, crash.ReproC),
2104+
ReproLogLink: textLink(textReproLog, crash.ReproLog),
2105+
ReproIsRevoked: crash.ReproIsRevoked,
2106+
MachineInfoLink: textLink(textMachineInfo, crash.MachineInfo),
2107+
Assets: makeUIAssets(c, build, crash, true),
20932108
}
20942109
if build != nil {
20952110
ui.uiBuild = makeUIBuild(c, build, true)

dashboard/app/templates/templates.html

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,14 @@ <h1><a href="/{{$.Namespace}}">syzbot</a></h1>
206206
<span class="bug-label">{{link .Link .Name}}</span>
207207
{{- end}}
208208
</td>
209-
<td class="stat">{{$b.ImpactScore}}</td>
209+
<td class="rank">
210+
{{if $b.RankTooltip}}
211+
<b>{{$b.ImpactScore}}</b>
212+
<pre class="tooltiptext">{{$b.RankTooltip}}</pre>
213+
{{else}}
214+
{{$b.ImpactScore}}
215+
{{end}}
216+
</td>
210217
<td class="stat">{{formatReproLevel $b.ReproLevel}}</td>
211218
<td class="bisect_status">{{print $b.BisectCause}}</td>
212219
<td class="bisect_status">{{print $b.BisectFix}}</td>
@@ -456,15 +463,25 @@ <h1><a href="/{{$.Namespace}}">syzbot</a></h1>
456463
<td class="tag">{{link $b.SyzkallerCommitLink (formatShortHash $b.SyzkallerCommit)}}</td>
457464
<td class="config">{{if $b.KernelConfigLink}}<a href="{{$b.KernelConfigLink}}">.config</a>{{end}}</td>
458465
<td class="repro">{{if $b.LogLink}}<a href="{{$b.LogLink}}">{{if $b.LogHasStrace}}strace{{else}}console{{end}} log</a>{{end}}</td>
459-
<td class="repro">{{if $b.ReportLink}}<a href="{{$b.ReportLink}}">report</a>{{end}}</td>
466+
<td class="repro">
467+
{{if $b.ReportLink}}<a href="{{$b.ReportLink}}">report</a>{{end}}
468+
{{range $i, $trl := $b.TailReportsLinks}}
469+
<br><a href="{{$trl}}">tail report {{$i}}</a>
470+
{{end}}
471+
</td>
460472
<td class="repro{{if $b.ReproIsRevoked}} stale_repro{{end}}">{{if $b.ReproSyzLink}}<a href="{{$b.ReproSyzLink}}">syz</a>{{end}}{{if $b.ReproLogLink}} / <a href="{{$b.ReproLogLink}}">log</a>{{end}}</td>
461473
<td class="repro{{if $b.ReproIsRevoked}} stale_repro{{end}}">{{if $b.ReproCLink}}<a href="{{$b.ReproCLink}}">C</a>{{end}}</td>
462474
<td class="repro">{{if $b.MachineInfoLink}}<a href="{{$b.MachineInfoLink}}">info</a>{{end}}</td>
463475
<td class="assets">{{range $i, $asset := .Assets}}
464476
<span class="no-break">[<a href="{{$asset.DownloadURL}}">{{$asset.Title}}</a>{{if $asset.FsckLogURL}} (<a href="{{$asset.FsckLogURL}}">{{if $asset.FsIsClean}}clean{{else}}corrupt{{end}} fs</a>){{end}}]</span>
465477
{{end}}</td>
466478
<td class="manager">{{$b.Manager}}</td>
467-
<td class="manager">{{$b.Title}}</td>
479+
<td class="manager">
480+
{{$b.Title}}
481+
{{range $tt := $b.TailTitles}}
482+
<br><a href="{{$tt}}">{{$tt}}</a>
483+
{{end}}
484+
</td>
468485
</tr>
469486
{{end}}
470487
</tbody>

dashboard/dashapi/dashapi.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,17 @@ type Crash struct {
322322
BuildID string // refers to Build.ID
323323
Title string
324324
AltTitles []string // alternative titles, used for better deduplication
325+
TailTitles []string // titles of the tail reports, see TailReports field
325326
Corrupted bool // report is corrupted (corrupted title, no stacks, etc)
326327
Suppressed bool
327328
Maintainers []string // deprecated in favor of Recipients
328329
Recipients Recipients
329330
Log []byte
330331
Flags CrashFlags
331332
Report []byte
333+
// Crashing machine may generate report chain like WARNING -> WARNING -> KASAN -> GPF.
334+
// These additional reports are used to better understand the bug nature and impact.
335+
TailReports [][]byte
332336
MachineInfo []byte
333337
Assets []NewAsset
334338
GuiltyFiles []string

pkg/manager/crash.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ func (cs *CrashStore) SaveCrash(crash *Crash) (bool, error) {
9191
writeOrRemove("tag", []byte(cs.Tag))
9292
writeOrRemove("report", report.MergeReportBytes(reps))
9393
writeOrRemove("machineInfo", crash.MachineInfo)
94-
if err := report.AddTitleStat(filepath.Join(dir, "title-stat"), reps); err != nil {
95-
return false, fmt.Errorf("report.AddTitleStat: %w", err)
94+
titleStatPath := filepath.Join(dir, "title-stat")
95+
titles := append([]string{crash.Title}, crash.TailTitles()...)
96+
if err := report.AddTitleToStatFile(titleStatPath, titles); err != nil {
97+
return false, fmt.Errorf("report.AddTitleToStatFile: %w", err)
9698
}
9799

98100
return first, nil
@@ -243,11 +245,13 @@ func (cs *CrashStore) BugInfo(id string, full bool) (*BugInfo, error) {
243245

244246
// Bug rank may go up over time if we observe higher ranked bugs as a consequence of the first failure.
245247
ret.Rank = report.TitlesToImpact(ret.Title)
246-
if titleStat, err := report.ReadStatFile(filepath.Join(dir, "title-stat")); err == nil {
247-
ret.TailTitles = report.ExplainTitleStat(titleStat)
248-
for _, ti := range ret.TailTitles {
249-
ret.Rank = max(ret.Rank, ti.Rank)
250-
}
248+
titleStat, err := report.ReadStatFile(filepath.Join(dir, "title-stat"))
249+
if err != nil {
250+
return nil, err
251+
}
252+
ret.TailTitles = titleStat.Explain()
253+
for _, ti := range ret.TailTitles {
254+
ret.Rank = max(ret.Rank, ti.Rank)
251255
}
252256

253257
ret.FirstTime = osutil.CreationTime(stat)

pkg/manager/http.go

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -357,34 +357,14 @@ func makeUICrashType(info *BugInfo, startTime time.Time, repros map[string]bool)
357357
info.ReproAttempts >= MaxReproAttempts)
358358
return UICrashType{
359359
BugInfo: *info,
360-
RankTooltip: higherRankTooltip(info.Title, info.TailTitles),
360+
RankTooltip: report.HigherRankTooltip(info.Title, info.TailTitles),
361361
New: info.FirstTime.After(startTime),
362362
Active: info.LastTime.After(startTime),
363363
Triaged: triaged,
364364
Crashes: crashes,
365365
}
366366
}
367367

368-
// higherRankTooltip generates the prioritized list of the titles with higher Rank
369-
// than the firstTitle has.
370-
func higherRankTooltip(firstTitle string, titlesInfo []*report.TitleFreqRank) string {
371-
baseRank := report.TitlesToImpact(firstTitle)
372-
res := ""
373-
for _, ti := range titlesInfo {
374-
if ti.Rank <= baseRank {
375-
continue
376-
}
377-
res += fmt.Sprintf("[rank %2v, freq %5.1f%%] %s\n",
378-
ti.Rank,
379-
100*float32(ti.Count)/float32(ti.Total),
380-
ti.Title)
381-
}
382-
if res != "" {
383-
return fmt.Sprintf("[rank %2v, originally] %s\n%s", baseRank, firstTitle, res)
384-
}
385-
return res
386-
}
387-
388368
var crashIDRe = regexp.MustCompile(`^\w+$`)
389369

390370
func (serv *HTTPServer) httpCrash(w http.ResponseWriter, r *http.Request) {

pkg/manager/repro.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ func (c *Crash) FullTitle() string {
5151
panic("the crash is expected to have a report")
5252
}
5353

54+
func (c *Crash) TailTitles() []string {
55+
res := make([]string, len(c.TailReports))
56+
for i, r := range c.TailReports {
57+
res[i] = r.Title
58+
}
59+
return res
60+
}
61+
62+
func (c *Crash) TailReportsBytes() [][]byte {
63+
res := make([][]byte, len(c.TailReports))
64+
for i, r := range c.TailReports {
65+
res[i] = r.Report
66+
}
67+
return res
68+
}
69+
5470
type ReproManagerView interface {
5571
RunRepro(ctx context.Context, crash *Crash) *ReproResult
5672
NeedRepro(crash *Crash) bool

0 commit comments

Comments
 (0)