aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2025-08-21 19:00:20 +0200
committerTaras Madan <tarasmadan@google.com>2025-08-28 11:36:44 +0000
commitd401b9d77980e7469e1c6eaa282f33df0fcfb3df (patch)
tree4a3f1285ece64120bbe75e5f336056adc77ef3dc /pkg
parent030f691787a240a447c5ec1491804160afa4050d (diff)
pkg/manager: add Rank column with tooltips to the main page
Diffstat (limited to 'pkg')
-rw-r--r--pkg/html/pages/style.css22
-rw-r--r--pkg/manager/crash.go12
-rw-r--r--pkg/manager/html/main.html9
-rw-r--r--pkg/manager/http.go41
-rw-r--r--pkg/report/impact_score.go44
-rw-r--r--pkg/report/title_stat.go6
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