aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2022-05-20 17:54:33 +0200
committerDmitry Vyukov <dvyukov@google.com>2022-05-20 20:06:53 +0200
commit7268fa62257981feeebc89e55b5ce45294beff8c (patch)
treeb408bea6cdbbd10669c990762595657f9893a7ee
parentf94bdf2730bcf53b45244e98aedb1a7a7711d49f (diff)
dashboard/app: linkify source files in sample reports
Fixes #652
-rw-r--r--dashboard/app/app_test.go23
-rw-r--r--dashboard/app/bug.html2
-rw-r--r--dashboard/app/main.go28
-rw-r--r--dashboard/app/static/style.css4
-rw-r--r--pkg/html/generated.go4
5 files changed, 54 insertions, 7 deletions
diff --git a/dashboard/app/app_test.go b/dashboard/app/app_test.go
index 9d2ff4dd7..bd512c803 100644
--- a/dashboard/app/app_test.go
+++ b/dashboard/app/app_test.go
@@ -12,6 +12,7 @@ import (
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"github.com/google/syzkaller/dashboard/dashapi"
"github.com/google/syzkaller/pkg/auth"
"github.com/google/syzkaller/sys/targets"
@@ -687,3 +688,25 @@ func compareBuilds(c *Ctx, dbBuild *Build, build *dashapi.Build) {
c.expectEQ(dbBuild.KernelCommit, build.KernelCommit)
c.expectEQ(dbBuild.SyzkallerCommit, build.SyzkallerCommit)
}
+
+func TestLinkifyReport(t *testing.T) {
+ input := `
+ tipc_topsrv_stop net/tipc/topsrv.c:694 [inline]
+ tipc_topsrv_exit_net+0x149/0x340 net/tipc/topsrv.c:715
+kernel BUG at fs/ext4/inode.c:2753!
+pkg/sentry/fsimpl/fuse/fusefs.go:278 +0x384
+ arch/x86/entry/entry_64.S:298
+`
+ // nolint: lll
+ output := `
+ tipc_topsrv_stop <a href='https://github.com/google/syzkaller/blob/111222/net/tipc/topsrv.c#L694'>net/tipc/topsrv.c:694</a> [inline]
+ tipc_topsrv_exit_net+0x149/0x340 <a href='https://github.com/google/syzkaller/blob/111222/net/tipc/topsrv.c#L715'>net/tipc/topsrv.c:715</a>
+kernel BUG at <a href='https://github.com/google/syzkaller/blob/111222/fs/ext4/inode.c#L2753'>fs/ext4/inode.c:2753</a>!
+<a href='https://github.com/google/syzkaller/blob/111222/pkg/sentry/fsimpl/fuse/fusefs.go#L278'>pkg/sentry/fsimpl/fuse/fusefs.go:278</a> +0x384
+ <a href='https://github.com/google/syzkaller/blob/111222/arch/x86/entry/entry_64.S#L298'>arch/x86/entry/entry_64.S:298</a>
+`
+ got := linkifyReport([]byte(input), "https://github.com/google/syzkaller", "111222")
+ if diff := cmp.Diff(output, string(got)); diff != "" {
+ t.Fatal(diff)
+ }
+}
diff --git a/dashboard/app/bug.html b/dashboard/app/bug.html
index 8b780dd19..0c3bd6a52 100644
--- a/dashboard/app/bug.html
+++ b/dashboard/app/bug.html
@@ -35,7 +35,7 @@ Page with details about a single bug.
{{if .SampleReport}}
<br><b>Sample crash report:</b><br>
- <div id="crash_div"><p><pre>{{printf "%s" .SampleReport}}</pre></p></div><br>
+ <div id="crash_div"><pre>{{.SampleReport}}</pre></div><br>
{{end}}
{{if .FixBisections}}
diff --git a/dashboard/app/main.go b/dashboard/app/main.go
index 621a2db16..79076f0b7 100644
--- a/dashboard/app/main.go
+++ b/dashboard/app/main.go
@@ -7,8 +7,10 @@ import (
"bytes"
"encoding/json"
"fmt"
+ "html/template"
"net/http"
"os"
+ "regexp"
"sort"
"strconv"
"strings"
@@ -130,7 +132,7 @@ type uiBugPage struct {
DupOf *uiBugGroup
Dups *uiBugGroup
Similar *uiBugGroup
- SampleReport []byte
+ SampleReport template.HTML
Crashes *uiCrashTable
FixBisections *uiCrashTable
TestPatchJobs *uiJobList
@@ -944,12 +946,12 @@ func updateBugBadness(c context.Context, bug *uiBug) {
bug.NumCrashesBad = bug.NumCrashes >= 10000 && timeNow(c).Sub(bug.LastTime) < 24*time.Hour
}
-func loadCrashesForBug(c context.Context, bug *Bug) ([]*uiCrash, []byte, error) {
+func loadCrashesForBug(c context.Context, bug *Bug) ([]*uiCrash, template.HTML, error) {
bugKey := bug.key(c)
// We can have more than maxCrashes crashes, if we have lots of reproducers.
crashes, _, err := queryCrashesForBug(c, bugKey, 2*maxCrashes+200)
if err != nil || len(crashes) == 0 {
- return nil, nil, err
+ return nil, "", err
}
builds := make(map[string]*Build)
var results []*uiCrash
@@ -958,7 +960,7 @@ func loadCrashesForBug(c context.Context, bug *Bug) ([]*uiCrash, []byte, error)
if build == nil {
build, err = loadBuild(c, bug.Namespace, crash.BuildID)
if err != nil {
- return nil, nil, err
+ return nil, "", err
}
builds[crash.BuildID] = build
}
@@ -966,11 +968,25 @@ func loadCrashesForBug(c context.Context, bug *Bug) ([]*uiCrash, []byte, error)
}
sampleReport, _, err := getText(c, textCrashReport, crashes[0].Report)
if err != nil {
- return nil, nil, err
+ return nil, "", err
}
- return results, sampleReport, nil
+ sampleBuild := builds[crashes[0].BuildID]
+ linkifiedReport := linkifyReport(sampleReport, sampleBuild.KernelRepo, sampleBuild.KernelCommit)
+ return results, linkifiedReport, nil
}
+func linkifyReport(report []byte, repo, commit string) template.HTML {
+ escaped := template.HTMLEscapeString(string(report))
+ return template.HTML(sourceFileRe.ReplaceAllStringFunc(escaped, func(match string) string {
+ sub := sourceFileRe.FindStringSubmatch(match)
+ line, _ := strconv.Atoi(sub[3])
+ url := vcs.FileLink(repo, commit, sub[2], line)
+ return fmt.Sprintf("%v<a href='%v'>%v:%v</a>%v", sub[1], url, sub[2], sub[3], sub[4])
+ }))
+}
+
+var sourceFileRe = regexp.MustCompile("( |\t|\n)([a-zA-Z0-9/_-]+\\.(?:h|c|cc|cpp|s|S|go|rs)):([0-9]+)( |!|\t|\n)")
+
func loadFixBisectionsForBug(c context.Context, bug *Bug) ([]*uiCrash, error) {
bugKey := bug.key(c)
jobs, _, err := queryJobsForBug(c, bugKey, JobBisectFix)
diff --git a/dashboard/app/static/style.css b/dashboard/app/static/style.css
index d627c70c5..7a2ca851a 100644
--- a/dashboard/app/static/style.css
+++ b/dashboard/app/static/style.css
@@ -280,6 +280,10 @@ aside {
background: transparent;
}
+#crash_div pre {
+ margin: 1px;
+}
+
.input-values {
margin-left: 7px;
margin-bottom: 7px;
diff --git a/pkg/html/generated.go b/pkg/html/generated.go
index c5e6f31ad..fed290590 100644
--- a/pkg/html/generated.go
+++ b/pkg/html/generated.go
@@ -284,6 +284,10 @@ aside {
background: transparent;
}
+#crash_div pre {
+ margin: 1px;
+}
+
.input-values {
margin-left: 7px;
margin-bottom: 7px;