Skip to content

Commit 84efa93

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 84efa93

File tree

16 files changed

+385
-196
lines changed

16 files changed

+385
-196
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/app_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import (
1616
"github.com/google/go-cmp/cmp"
1717
"github.com/google/syzkaller/dashboard/dashapi"
1818
"github.com/google/syzkaller/pkg/auth"
19+
"github.com/google/syzkaller/pkg/report"
1920
"github.com/google/syzkaller/pkg/subsystem"
2021
_ "github.com/google/syzkaller/pkg/subsystem/lists"
2122
"github.com/google/syzkaller/sys/targets"
23+
"github.com/stretchr/testify/assert"
2224
"google.golang.org/appengine/v2/user"
2325
)
2426

@@ -1139,3 +1141,43 @@ kernel BUG at <a href='https://github.com/google/syzkaller/blob/111222/fs/ext4/i
11391141
t.Fatal(diff)
11401142
}
11411143
}
1144+
1145+
func TestTailReports(t *testing.T) {
1146+
c := NewCtx(t)
1147+
defer c.Close()
1148+
1149+
build := testBuild(1)
1150+
err := c.client.UploadBuild(build)
1151+
assert.NoError(t, err)
1152+
1153+
crash := testCrash(build, 1)
1154+
_, err = c.client.ReportCrash(crash)
1155+
assert.NoError(t, err)
1156+
c.client.pollBug()
1157+
1158+
const someKASANTitle = "KASAN: slab-out-of-bounds Write in foo"
1159+
crash.TailTitles = []string{someKASANTitle}
1160+
crash.TailReports = [][]byte{[]byte("tail report")}
1161+
_, err = c.client.ReportCrash(crash)
1162+
assert.NoError(t, err)
1163+
1164+
res, _ := c.GET("/test1")
1165+
wantRank := fmt.Sprintf("[rank %d, freq 50.0%%] %s", report.TitlesToImpact(someKASANTitle), someKASANTitle)
1166+
if !strings.Contains(string(res), wantRank) {
1167+
t.Logf("%s", res)
1168+
t.Errorf("can't find rank string %q", wantRank)
1169+
}
1170+
1171+
res, _ = c.GET("/bug?extid=decf42d66dced481afc1")
1172+
1173+
// Bug page crash table has 2 lines. Checking the most complex only.
1174+
wantTitles := fmt.Sprintf(">%s<br>%s<", crash.Title, crash.TailTitles[0])
1175+
if !strings.Contains(string(res), wantTitles) {
1176+
t.Logf("%s", res)
1177+
t.Errorf("can't find titles string %q", wantTitles)
1178+
}
1179+
if !strings.Contains(string(res), ">tail report 1<") {
1180+
t.Logf("%s", res)
1181+
t.Error("can't find tail report string")
1182+
}
1183+
}

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 `datastore:",noindex"` // 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(append([]string{bug.Title}, 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: 22 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,27 @@ <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 -}}
468+
<a href="{{$b.ReportLink}}">report</a>
469+
{{- end -}}
470+
{{- range $i, $trl := $b.TailReportsLinks -}}
471+
<br><a href="{{$trl}}">tail report {{add $i 1}}</a>
472+
{{- end -}}
473+
</td>
460474
<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>
461475
<td class="repro{{if $b.ReproIsRevoked}} stale_repro{{end}}">{{if $b.ReproCLink}}<a href="{{$b.ReproCLink}}">C</a>{{end}}</td>
462476
<td class="repro">{{if $b.MachineInfoLink}}<a href="{{$b.MachineInfoLink}}">info</a>{{end}}</td>
463477
<td class="assets">{{range $i, $asset := .Assets}}
464478
<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>
465479
{{end}}</td>
466480
<td class="manager">{{$b.Manager}}</td>
467-
<td class="manager">{{$b.Title}}</td>
481+
<td class="manager">
482+
{{- $b.Title}}
483+
{{- range $tt := $b.TailTitles -}}
484+
<br>{{$tt}}
485+
{{- end -}}
486+
</td>
468487
</tr>
469488
{{end}}
470489
</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/html/html.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,27 @@ func CreateTextGlob(glob string) *texttemplate.Template {
5252
}
5353

5454
var Funcs = template.FuncMap{
55-
"link": link,
56-
"optlink": optlink,
57-
"formatTime": FormatTime,
58-
"formatDate": FormatDate,
59-
"formatKernelTime": formatKernelTime,
60-
"formatJSTime": formatJSTime,
55+
// keep-sorted start
56+
"add": func(a, b int) int { return a + b },
57+
"commitLink": commitLink,
58+
"dereference": dereferencePointer,
6159
"formatClock": formatClock,
60+
"formatCommitTableTitle": formatCommitTableTitle,
61+
"formatDate": FormatDate,
6262
"formatDuration": formatDuration,
63+
"formatJSTime": formatJSTime,
64+
"formatKernelTime": formatKernelTime,
6365
"formatLateness": formatLateness,
66+
"formatList": formatStringList,
6467
"formatReproLevel": formatReproLevel,
65-
"formatStat": formatStat,
6668
"formatShortHash": formatShortHash,
69+
"formatStat": formatStat,
6770
"formatTagHash": formatTagHash,
68-
"formatCommitTableTitle": formatCommitTableTitle,
69-
"formatList": formatStringList,
71+
"formatTime": FormatTime,
72+
"link": link,
73+
"optlink": optlink,
7074
"selectBisect": selectBisect,
71-
"dereference": dereferencePointer,
72-
"commitLink": commitLink,
75+
// keep-sorted end
7376
}
7477

7578
func selectBisect(rep *dashapi.BugReport) *dashapi.BisectResult {

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.AddTitlesToStatFile(titleStatPath, titles); err != nil {
97+
return false, fmt.Errorf("report.AddTitlesToStatFile: %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)

0 commit comments

Comments
 (0)