github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-fillreports/fillreports.go (about) 1 // Copyright 2023 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 // syz-fillreports queries all open bugs from a namespace, extracts the missing reporting elements 5 // (currently only missing guilty files are supported) and uploads them back to the dashboard. 6 7 package main 8 9 import ( 10 "flag" 11 "log" 12 "sync" 13 14 "github.com/google/syzkaller/dashboard/dashapi" 15 "github.com/google/syzkaller/pkg/mgrconfig" 16 "github.com/google/syzkaller/pkg/report" 17 _ "github.com/google/syzkaller/sys" 18 "github.com/google/syzkaller/sys/targets" 19 ) 20 21 var ( 22 flagDashboard = flag.String("dashboard", "https://syzkaller.appspot.com", "dashboard address") 23 flagAPIClient = flag.String("client", "", "the name of the API client") 24 flagAPIKey = flag.String("key", "", "api key") 25 ) 26 27 func main() { 28 flag.Parse() 29 30 dash, err := dashapi.New(*flagAPIClient, *flagDashboard, *flagAPIKey) 31 if err != nil { 32 log.Fatalf("dashapi failed: %v", err) 33 } 34 resp, err := dash.BugList() 35 if err != nil { 36 log.Fatalf("bug list query failed: %v", err) 37 } 38 workItems := loadBugReports(dash, resp.List) 39 for item := range workItems { 40 processReport(dash, item.report, item.bugID) 41 } 42 } 43 44 func processReport(dash *dashapi.Dashboard, bugReport *dashapi.BugReport, bugID string) { 45 if bugReport.ReportElements != nil && len(bugReport.ReportElements.GuiltyFiles) > 0 { 46 log.Printf("%v: already has guilty files", bugReport.ID) 47 return 48 } 49 if bugReport.BugStatus != dashapi.BugStatusOpen && 50 bugReport.BugStatus != dashapi.BugStatusFixed { 51 log.Printf("%v: status is not BugStatusOpen or BugStatusFixed", bugReport.ID) 52 return 53 } 54 if bugReport.OS == "" || bugReport.Arch == "" { 55 log.Printf("%v: OS or Arch is empty", bugReport.ID) 56 return 57 } 58 cfg := &mgrconfig.Config{ 59 Derived: mgrconfig.Derived{ 60 TargetOS: bugReport.OS, 61 TargetArch: bugReport.Arch, 62 SysTarget: targets.Get(bugReport.OS, bugReport.Arch), 63 }, 64 } 65 reporter, err := report.NewReporter(cfg) 66 if err != nil { 67 log.Fatalf("%v: failed to create a reporter for %s/%s", 68 bugReport.ID, bugReport.OS, bugReport.Arch) 69 } 70 guiltyFile := reporter.ReportToGuiltyFile(bugReport.Title, bugReport.Report) 71 if guiltyFile == "" { 72 log.Printf("%v: no guilty files extracted", bugReport.ID) 73 return 74 } 75 err = dash.UpdateReport(&dashapi.UpdateReportReq{ 76 BugID: bugID, 77 CrashID: bugReport.CrashID, 78 GuiltyFiles: &[]string{guiltyFile}, 79 }) 80 if err != nil { 81 log.Printf("%v: failed to save: %v", bugReport.ID, err) 82 } 83 log.Printf("%v: updated", bugReport.ID) 84 } 85 86 type workItem struct { 87 report *dashapi.BugReport 88 bugID string 89 } 90 91 func loadBugReports(dash *dashapi.Dashboard, IDs []string) <-chan *workItem { 92 const ( 93 threads = 8 94 logStep = 100 95 ) 96 ids := make(chan string) 97 ret := make(chan *workItem) 98 var wg sync.WaitGroup 99 for i := 0; i < threads; i++ { 100 wg.Add(1) 101 go func() { 102 defer wg.Done() 103 for id := range ids { 104 resp, err := dash.LoadBug(id) 105 if err != nil { 106 log.Printf("%v: failed to load bug: %v", id, err) 107 continue 108 } 109 if resp.ID == "" { 110 continue 111 } 112 ret <- &workItem{ 113 report: resp, 114 bugID: id, 115 } 116 } 117 }() 118 } 119 go func() { 120 for i, id := range IDs { 121 if i%logStep == 0 { 122 log.Printf("loaded %d/%d", i, len(IDs)) 123 } 124 ids <- id 125 } 126 close(ids) 127 wg.Wait() 128 close(ret) 129 }() 130 return ret 131 }