From 7268fa62257981feeebc89e55b5ce45294beff8c Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 20 May 2022 17:54:33 +0200 Subject: dashboard/app: linkify source files in sample reports Fixes #652 --- dashboard/app/app_test.go | 23 +++++++++++++++++++++++ dashboard/app/bug.html | 2 +- dashboard/app/main.go | 28 ++++++++++++++++++++++------ dashboard/app/static/style.css | 4 ++++ pkg/html/generated.go | 4 ++++ 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 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 +` + 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}}
Sample crash report:
-

{{printf "%s" .SampleReport}}


+
{{.SampleReport}}

{{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%v:%v%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; -- cgit mrf-deployment