diff options
| -rw-r--r-- | pkg/html/pages/style.css | 22 | ||||
| -rw-r--r-- | pkg/manager/crash.go | 12 | ||||
| -rw-r--r-- | pkg/manager/html/main.html | 9 | ||||
| -rw-r--r-- | pkg/manager/http.go | 41 | ||||
| -rw-r--r-- | pkg/report/impact_score.go | 44 | ||||
| -rw-r--r-- | pkg/report/title_stat.go | 6 |
6 files changed, 122 insertions, 12 deletions
diff --git a/pkg/html/pages/style.css b/pkg/html/pages/style.css index 5da7359e6..aecc40b7e 100644 --- a/pkg/html/pages/style.css +++ b/pkg/html/pages/style.css @@ -177,6 +177,12 @@ table td, table th { text-align: right; } +.list_table .rank { + width: 55pt; + font-family: monospace; + text-align: right; +} + .list_table .discussions { font-family: monospace; text-align: left; @@ -493,3 +499,19 @@ aside { /* Change the background color of the dropdown button when the dropdown content is shown */ .dropdown:hover .dropbtn {background-color: #ddd;} + +.rank .tooltiptext { + visibility: hidden; + background-color: black; + color: #fff; + text-align: left; + border-radius: 6px; + padding: 5px 0; + + /* Position the tooltip */ + position: absolute; + z-index: 1; +} +.rank:hover .tooltiptext { + visibility: visible; +}
\ No newline at end of file diff --git a/pkg/manager/crash.go b/pkg/manager/crash.go index f0fa58959..a62ec8280 100644 --- a/pkg/manager/crash.go +++ b/pkg/manager/crash.go @@ -216,6 +216,7 @@ type CrashInfo struct { type BugInfo struct { ID string Title string + TailTitles []*report.TitleFreqRank FirstTime time.Time LastTime time.Time HasRepro bool @@ -223,6 +224,7 @@ type BugInfo struct { StraceFile string // relative to the workdir ReproAttempts int Crashes []*CrashInfo + Rank int } func (cs *CrashStore) BugInfo(id string, full bool) (*BugInfo, error) { @@ -238,6 +240,16 @@ func (cs *CrashStore) BugInfo(id string, full bool) (*BugInfo, error) { return nil, err } ret.Title = strings.TrimSpace(string(desc)) + + // Bug rank may go up over time if we observe higher ranked bugs as a consequence of the first failure. + ret.Rank = report.TitlesToImpact(ret.Title) + if titleStat, err := report.ReadStatFile(filepath.Join(dir, "title-stat")); err == nil { + ret.TailTitles = report.ExplainTitleStat(titleStat) + for _, ti := range ret.TailTitles { + ret.Rank = max(ret.Rank, ti.Rank) + } + } + ret.FirstTime = osutil.CreationTime(stat) ret.LastTime = stat.ModTime() files, err := osutil.ListDir(dir) diff --git a/pkg/manager/html/main.html b/pkg/manager/html/main.html index 42e92ac2b..43652cbf6 100644 --- a/pkg/manager/html/main.html +++ b/pkg/manager/html/main.html @@ -26,6 +26,7 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the <thead> <tr> <th><a onclick="return sortTable(this, 'Description', textSort)" href="#">Description</a></th> + <th><a onclick="return sortTable(this, 'Rank', numSort)" href="#">Rank</a></th> <th><a onclick="return sortTable(this, 'Count', numSort)" href="#">Count</a></th> <th><a onclick="return sortTable(this, 'First Time', textSort, true)" href="#">First Time</a></th> <th><a onclick="return sortTable(this, 'Last Time', textSort, true)" href="#">Last Time</a></th> @@ -37,6 +38,14 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the {{range $c := $.Crashes}} <tr> <td class="title"><a href="/crash?id={{$c.ID}}">{{$c.Title}}</a></td> + <td class="rank {{if not $c.Active}}inactive{{end}}"> + {{if $c.RankTooltip}} + <b>{{$c.Rank}}</b> + <pre class="tooltiptext">{{$c.RankTooltip}}</pre> + {{else}} + {{$c.Rank}} + {{end}} + </td> <td class="stat {{if not $c.Active}}inactive{{end}}">{{len $c.Crashes}}</td> <td class="time {{if not $c.New}}inactive{{end}}">{{formatTime $c.FirstTime}}</td> <td class="time {{if not $c.Active}}inactive{{end}}">{{formatTime $c.LastTime}}</td> diff --git a/pkg/manager/http.go b/pkg/manager/http.go index e6a7d6b46..61d208f41 100644 --- a/pkg/manager/http.go +++ b/pkg/manager/http.go @@ -29,6 +29,7 @@ import ( "github.com/google/syzkaller/pkg/html/pages" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/mgrconfig" + "github.com/google/syzkaller/pkg/report" "github.com/google/syzkaller/pkg/stat" "github.com/google/syzkaller/pkg/vcs" "github.com/google/syzkaller/pkg/vminfo" @@ -355,12 +356,33 @@ func makeUICrashType(info *BugInfo, startTime time.Time, repros map[string]bool) triaged := reproStatus(info.HasRepro, info.HasCRepro, repros[info.Title], info.ReproAttempts >= MaxReproAttempts) return UICrashType{ - BugInfo: *info, - New: info.FirstTime.After(startTime), - Active: info.LastTime.After(startTime), - Triaged: triaged, - Crashes: crashes, + BugInfo: *info, + RankTooltip: higherRankTooltip(info.Title, info.TailTitles), + New: info.FirstTime.After(startTime), + Active: info.LastTime.After(startTime), + Triaged: triaged, + Crashes: crashes, + } +} + +// higherRankTooltip generates the prioritized list of the titles with higher Rank +// than the firstTitle has. +func higherRankTooltip(firstTitle string, titlesInfo []*report.TitleFreqRank) string { + baseRank := report.TitlesToImpact(firstTitle) + res := "" + for _, ti := range titlesInfo { + if ti.Rank <= baseRank { + continue + } + res += fmt.Sprintf("[rank %2v, freq %5.1f%%] %s\n", + ti.Rank, + 100*float32(ti.Count)/float32(ti.Total), + ti.Title) + } + if res != "" { + return fmt.Sprintf("[rank %2v, originally] %s\n%s", baseRank, firstTitle, res) } + return res } var crashIDRe = regexp.MustCompile(`^\w+$`) @@ -1024,10 +1046,11 @@ type UICrashPage struct { type UICrashType struct { BugInfo - New bool // was first found in the current run - Active bool // was found in the current run - Triaged string - Crashes []UICrash + RankTooltip string + New bool // was first found in the current run + Active bool // was found in the current run + Triaged string + Crashes []UICrash } type UICrash struct { diff --git a/pkg/report/impact_score.go b/pkg/report/impact_score.go index 63adb652a..8139644d2 100644 --- a/pkg/report/impact_score.go +++ b/pkg/report/impact_score.go @@ -4,6 +4,8 @@ package report import ( + "sort" + "github.com/google/syzkaller/pkg/report/crash" ) @@ -62,3 +64,45 @@ func TitlesToImpact(title string, otherTitles ...string) int { } return maxImpact } + +type TitleFreqRank struct { + Title string + Count int + Total int + Rank int +} + +func ExplainTitleStat(ts *titleStat) []*TitleFreqRank { + titleCount := map[string]int{} + var totalCount int + ts.visit(func(count int, titles ...string) { + uniq := map[string]bool{} + for _, title := range titles { + uniq[title] = true + } + for title := range uniq { + titleCount[title] += count + } + totalCount += count + }) + var res []*TitleFreqRank + for title, count := range titleCount { + res = append(res, &TitleFreqRank{ + Title: title, + Count: count, + Total: totalCount, + Rank: TitlesToImpact(title), + }) + } + sort.Slice(res, func(l, r int) bool { + if res[l].Rank != res[r].Rank { + return res[l].Rank > res[r].Rank + } + lTitle, rTitle := res[l].Title, res[r].Title + if titleCount[lTitle] != titleCount[rTitle] { + return titleCount[lTitle] > titleCount[rTitle] + } + return lTitle < rTitle + }) + return res +} diff --git a/pkg/report/title_stat.go b/pkg/report/title_stat.go index 92d8079d5..5b3cd3fb7 100644 --- a/pkg/report/title_stat.go +++ b/pkg/report/title_stat.go @@ -16,9 +16,9 @@ func AddTitleStat(file string, reps []*Report) error { for _, rep := range reps { titles = append(titles, rep.Title) } - stat, err := readStatFile(file) + stat, err := ReadStatFile(file) if err != nil { - return fmt.Errorf("readStatFile: %w", err) + return fmt.Errorf("report.ReadStatFile: %w", err) } stat.add(titles) if err := writeStatFile(file, stat); err != nil { @@ -27,7 +27,7 @@ func AddTitleStat(file string, reps []*Report) error { return nil } -func readStatFile(file string) (*titleStat, error) { +func ReadStatFile(file string) (*titleStat, error) { stat := &titleStat{} if _, err := os.Stat(file); errors.Is(err, os.ErrNotExist) { return stat, nil |
