diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2025-11-28 18:01:28 +0100 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2025-12-29 10:47:28 +0000 |
| commit | ba994aabc89d9006610a4c6e1752eeb0b639242c (patch) | |
| tree | 9e3b80d6b5758fffc5745091fc239966c03ffc15 /dashboard/app | |
| parent | 9831da75d9d8d1cb90bcd1f16ee490d641201802 (diff) | |
dashboard: factor out uiBugDetails and loadBugDetails
Separate the code that loads all details about a particular bug from the
code that is only necessary for rendering a bug page on Web UI.
These can be used in mass bug exports.
Diffstat (limited to 'dashboard/app')
| -rw-r--r-- | dashboard/app/main.go | 227 | ||||
| -rw-r--r-- | dashboard/app/public_json_api.go | 20 | ||||
| -rw-r--r-- | dashboard/app/templates/bug.html | 12 |
3 files changed, 138 insertions, 121 deletions
diff --git a/dashboard/app/main.go b/dashboard/app/main.go index dbf95d502..2fb4a14fb 100644 --- a/dashboard/app/main.go +++ b/dashboard/app/main.go @@ -290,16 +290,29 @@ type uiReproAttempt struct { type uiBugPage struct { Header *uiHeader Now time.Time - Bug *uiBug - BisectCause *uiJob - BisectFix *uiJob - FixCandidate *uiJob Sections []*uiCollapsible - SampleReport template.HTML Crashes *uiCrashTable - TestPatchJobs *uiJobList LabelGroups []*uiBugLabelGroup DebugSubsystems string + Bug *uiBugDetails +} + +type uiBugDetails struct { + *uiBug + // If DupOf is not nil, uiBug is a duplicate of DupOf. + DupOf *uiBug + // Dups are the bugs that have been deduplicated into uiBug. + Dups *uiBugGroup + // Similar are the bugs from other namespaces that have the same title. + Similar *uiBugGroup + BisectCauseJob *uiJob + BisectCauseJobs []*uiJob + BisectFixJob *uiJob + BisectFixJobs []*uiJob + FixCandidateJob *uiJob + SampleReport template.HTML + Crashes []*uiCrash + TestPatchJobs *uiJobList } type uiBugLabelGroup struct { @@ -1070,46 +1083,27 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error if err != nil { return err } - state, err := loadReportingState(c) - if err != nil { - return err - } - managers, err := CachedManagerList(c, bug.Namespace) + bugDetails, err := loadBugDetails(c, bug, accessLevel) if err != nil { return err } sections := []*uiCollapsible{} - if bug.DupOf != "" { - dup := new(Bug) - if err := db.Get(c, db.NewKey(c, "Bug", bug.DupOf, 0, nil), dup); err != nil { - return err - } - if accessLevel >= dup.sanitizeAccess(c, accessLevel) { - sections = append(sections, &uiCollapsible{ - Title: "Duplicate of", - Show: true, - Type: sectionBugList, - Value: &uiBugGroup{ - Now: timeNow(c), - Bugs: []*uiBug{createUIBug(c, dup, state, managers)}, - }, - }) - } - } - uiBug := createUIBug(c, bug, state, managers) - crashes, sampleReport, err := loadCrashesForBug(c, bug) - if err != nil { - return err + if bugDetails.DupOf != nil { + sections = append(sections, &uiCollapsible{ + Title: "Duplicate of", + Show: true, + Type: sectionBugList, + Value: &uiBugGroup{ + Now: timeNow(c), + Bugs: []*uiBug{bugDetails.DupOf}, + }, + }) } crashesTable := &uiCrashTable{ - Crashes: crashes, - Caption: fmt.Sprintf("Crashes (%d)", bug.NumCrashes), + Crashes: bugDetails.Crashes, + Caption: fmt.Sprintf("Crashes (%d)", bugDetails.NumCrashes), } - dups, err := loadDupsForBug(c, r, bug, state, managers) - if err != nil { - return err - } - if len(dups.Bugs) > 0 { + if dups := bugDetails.Dups; len(dups.Bugs) > 0 { sections = append(sections, &uiCollapsible{ Title: fmt.Sprintf("Duplicate bugs (%d)", len(dups.Bugs)), Type: sectionBugList, @@ -1140,11 +1134,7 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error Value: treeTestJobs, }) } - similar, err := loadSimilarBugsUI(c, r, bug, state) - if err != nil { - return err - } - if len(similar.Bugs) > 0 { + if similar := bugDetails.Similar; len(similar.Bugs) > 0 { sections = append(sections, &uiCollapsible{ Title: fmt.Sprintf("Similar bugs (%d)", len(similar.Bugs)), Show: getNsConfig(c, hdr.Namespace).AccessLevel != AccessPublic, @@ -1152,35 +1142,6 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error Value: similar, }) } - causeBisections, err := queryBugJobs(c, bug, JobBisectCause) - if err != nil { - return fmt.Errorf("failed to load cause bisections: %w", err) - } - var bisectCause *uiJob - if bug.BisectCause > BisectPending { - bisectCause, err = causeBisections.uiBestBisection(c) - if err != nil { - return err - } - } - fixBisections, err := queryBugJobs(c, bug, JobBisectFix) - if err != nil { - return fmt.Errorf("failed to load cause bisections: %w", err) - } - var bisectFix *uiJob - if bug.BisectFix > BisectPending { - bisectFix, err = fixBisections.uiBestBisection(c) - if err != nil { - return err - } - } - var fixCandidate *uiJob - if bug.FixCandidateJob != "" { - fixCandidate, err = fixBisections.uiBestFixCandidate(c) - if err != nil { - return err - } - } testPatchJobs, err := loadTestPatchJobs(c, bug) if err != nil { return err @@ -1204,16 +1165,12 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error }) } data := &uiBugPage{ - Header: hdr, - Now: timeNow(c), - Bug: uiBug, - BisectCause: bisectCause, - BisectFix: bisectFix, - FixCandidate: fixCandidate, - Sections: sections, - SampleReport: sampleReport, - Crashes: crashesTable, - LabelGroups: getLabelGroups(c, bug), + Header: hdr, + Now: timeNow(c), + Sections: sections, + LabelGroups: getLabelGroups(c, bug), + Crashes: crashesTable, + Bug: bugDetails, } if accessLevel == AccessAdmin && !bug.hasUserSubsystems() { data.DebugSubsystems = urlutil.SetParam(data.Bug.Link, "debug_subsystems", "1") @@ -1222,26 +1179,16 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error // - no fix bisections have been performed on the bug // - fix bisection was performed but resulted in a crash on HEAD // - there have been infrastructure problems during the job execution - if len(fixBisections.all()) > 1 || len(fixBisections.all()) > 0 && bisectFix == nil { - uiList, err := fixBisections.uiAll(c) - if err != nil { - return err - } - if len(uiList) != 0 { - data.Sections = append(data.Sections, makeCollapsibleBugJobs( - "Fix bisection attempts", uiList)) - } + // nolint: dupl + if len(bugDetails.BisectFixJobs) > 1 || len(bugDetails.BisectFixJobs) > 0 && bugDetails.BisectFixJob == nil { + data.Sections = append(data.Sections, makeCollapsibleBugJobs( + "Fix bisection attempts", bugDetails.BisectFixJobs)) } // Similarly, a cause bisection can be repeated if there were infrastructure problems. - if len(causeBisections.all()) > 1 || len(causeBisections.all()) > 0 && bisectCause == nil { - uiList, err := causeBisections.uiAll(c) - if err != nil { - return err - } - if len(uiList) != 0 { - data.Sections = append(data.Sections, makeCollapsibleBugJobs( - "Cause bisection attempts", uiList)) - } + // nolint: dupl + if len(bugDetails.BisectCauseJobs) > 1 || len(bugDetails.BisectCauseJobs) > 0 && bugDetails.BisectCauseJob == nil { + data.Sections = append(data.Sections, makeCollapsibleBugJobs( + "Cause bisection attempts", bugDetails.BisectCauseJobs)) } if r.FormValue("json") == "1" { w.Header().Set("Content-Type", "application/json") @@ -1251,6 +1198,76 @@ func handleBug(c context.Context, w http.ResponseWriter, r *http.Request) error return serveTemplate(w, "bug.html", data) } +func loadBugDetails(c context.Context, bug *Bug, accessLevel AccessLevel) (*uiBugDetails, error) { + managers, err := CachedManagerList(c, bug.Namespace) + if err != nil { + return nil, err + } + state, err := loadReportingState(c) + if err != nil { + return nil, err + } + ret := &uiBugDetails{ + uiBug: createUIBug(c, bug, state, managers), + } + if bug.DupOf != "" { + dup := new(Bug) + if err := db.Get(c, db.NewKey(c, "Bug", bug.DupOf, 0, nil), dup); err != nil { + return nil, err + } + if accessLevel >= dup.sanitizeAccess(c, accessLevel) { + ret.DupOf = createUIBug(c, dup, state, managers) + } + } + ret.Crashes, ret.SampleReport, err = loadCrashesForBug(c, bug) + if err != nil { + return nil, err + } + ret.Dups, err = loadDupsForBug(c, bug, state, managers, accessLevel) + if err != nil { + return nil, err + } + ret.Similar, err = loadSimilarBugsUI(c, bug, state, accessLevel) + if err != nil { + return nil, err + } + causeBisections, err := queryBugJobs(c, bug, JobBisectCause) + if err != nil { + return nil, fmt.Errorf("failed to load cause bisections: %w", err) + } + ret.BisectCauseJobs, err = causeBisections.uiAll(c) + if err != nil { + return nil, fmt.Errorf("failed to load all fix bisections: %w", err) + } + if bug.BisectCause > BisectPending { + ret.BisectCauseJob, err = causeBisections.uiBestBisection(c) + if err != nil { + return nil, err + } + } + fixBisections, err := queryBugJobs(c, bug, JobBisectFix) + if err != nil { + return nil, fmt.Errorf("failed to load fix bisections: %w", err) + } + ret.BisectFixJobs, err = fixBisections.uiAll(c) + if err != nil { + return nil, fmt.Errorf("failed to load all fix bisections: %w", err) + } + if bug.BisectFix > BisectPending { + ret.BisectFixJob, err = fixBisections.uiBestBisection(c) + if err != nil { + return nil, err + } + } + if bug.FixCandidateJob != "" { + ret.FixCandidateJob, err = fixBisections.uiBestFixCandidate(c) + if err != nil { + return nil, err + } + } + return ret, nil +} + func getReproAttempts(bug *Bug) []*uiReproAttempt { var ret []*uiReproAttempt for _, item := range bug.ReproAttempts { @@ -1816,7 +1833,8 @@ func applyBugFilter(query *db.Query, filter *userBugFilter) *db.Query { return query } -func loadDupsForBug(c context.Context, r *http.Request, bug *Bug, state *ReportingState, managers []string) ( +func loadDupsForBug(c context.Context, bug *Bug, state *ReportingState, + managers []string, accessLevel AccessLevel) ( *uiBugGroup, error) { bugHash := bug.keyHash(c) var dups []*Bug @@ -1828,7 +1846,6 @@ func loadDupsForBug(c context.Context, r *http.Request, bug *Bug, state *Reporti return nil, err } var results []*uiBug - accessLevel := accessLevel(c, r) for _, dup := range dups { if accessLevel < dup.sanitizeAccess(c, accessLevel) { continue @@ -1845,9 +1862,9 @@ func loadDupsForBug(c context.Context, r *http.Request, bug *Bug, state *Reporti return group, nil } -func loadSimilarBugsUI(c context.Context, r *http.Request, bug *Bug, state *ReportingState) (*uiBugGroup, error) { +func loadSimilarBugsUI(c context.Context, bug *Bug, state *ReportingState, + accessLevel AccessLevel) (*uiBugGroup, error) { managers := make(map[string][]string) - accessLevel := accessLevel(c, r) similarBugs, err := loadSimilarBugs(c, bug) if err != nil { return nil, err diff --git a/dashboard/app/public_json_api.go b/dashboard/app/public_json_api.go index 75c688954..104ee42a1 100644 --- a/dashboard/app/public_json_api.go +++ b/dashboard/app/public_json_api.go @@ -16,23 +16,23 @@ import ( "github.com/google/syzkaller/pkg/coveragedb" ) -func getExtAPIDescrForBugPage(bugPage *uiBugPage) *api.Bug { +func getExtAPIDescrForBug(bug *uiBugDetails) *api.Bug { return &api.Bug{ Version: api.Version, - Title: bugPage.Bug.Title, - ID: bugPage.Bug.ID, + Title: bug.Title, + ID: bug.ID, Discussions: func() []string { - if bugPage.Bug.ExternalLink == "" { + if bug.ExternalLink == "" { return nil } - return []string{bugPage.Bug.ExternalLink} + return []string{bug.ExternalLink} }(), - FixCommits: getBugFixCommits(bugPage.Bug), + FixCommits: getBugFixCommits(bug.uiBug), CauseCommit: func() *api.Commit { - if bugPage.BisectCause == nil || bugPage.BisectCause.Commit == nil { + if bug.BisectCause == nil || bug.BisectCause.Commit == nil { return nil } - bisectCause := bugPage.BisectCause + bisectCause := bug.BisectCause return &api.Commit{ Title: bisectCause.Commit.Title, Link: bisectCause.Commit.Link, @@ -42,7 +42,7 @@ func getExtAPIDescrForBugPage(bugPage *uiBugPage) *api.Bug { }(), Crashes: func() []api.Crash { var res []api.Crash - for _, crash := range bugPage.Crashes.Crashes { + for _, crash := range bug.Crashes { res = append(res, api.Crash{ Title: crash.Title, SyzReproducerLink: crash.ReproSyzLink, @@ -165,7 +165,7 @@ func GetJSONDescrFor(page any) ([]byte, error) { var res any switch i := page.(type) { case *uiBugPage: - res = getExtAPIDescrForBugPage(i) + res = getExtAPIDescrForBug(i.Bug) case *uiTerminalPage: res = getExtAPIDescrForBugGroups([]*uiBugGroup{i.Bugs}) case *uiMainPage: diff --git a/dashboard/app/templates/bug.html b/dashboard/app/templates/bug.html index d64f71e82..94de735b4 100644 --- a/dashboard/app/templates/bug.html +++ b/dashboard/app/templates/bug.html @@ -38,12 +38,12 @@ Page with details about a single bug. {{end}} {{end}} First crash: {{formatLateness $.Now $.Bug.FirstTime}}, last: {{formatLateness $.Now $.Bug.LastTime}}<br> - {{if .FixCandidate}} - <div class="fix-candidate-block">{{template "bisect_results" .FixCandidate}}</div> + {{if .Bug.FixCandidateJob}} + <div class="fix-candidate-block">{{template "bisect_results" .Bug.FixCandidateJob}}</div> {{end}} <div> - {{if .BisectCause}}<div class="bug-bisection-info">{{template "bisect_results" .BisectCause}}</div>{{end}} - {{if .BisectFix}}<div class="bug-bisection-info">{{template "bisect_results" .BisectFix}}</div>{{end}} + {{if .Bug.BisectCauseJob}}<div class="bug-bisection-info">{{template "bisect_results" .Bug.BisectCauseJob}}</div>{{end}} + {{if .Bug.BisectFixJob}}<div class="bug-bisection-info">{{template "bisect_results" .Bug.BisectFixJob}}</div>{{end}} <div class="bug-bisection-stop"></div> </div> @@ -64,9 +64,9 @@ Page with details about a single bug. </div> {{end}} - {{if .SampleReport}} + {{if .Bug.SampleReport}} <br><b>Sample crash report:</b><br> - <div id="crash_div"><pre>{{.SampleReport}}</pre></div><br> + <div id="crash_div"><pre>{{.Bug.SampleReport}}</pre></div><br> {{end}} {{template "crash_list" .Crashes}} |
