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  }