github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/tools/syz-cover/syz-cover.go (about) 1 // Copyright 2018 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-cover generates coverage HTML report from raw coverage files. 5 // Raw coverage files are text files with one PC in hex form per line, e.g.: 6 // 7 // 0xffffffff8398658d 8 // 0xffffffff839862fc 9 // 0xffffffff8398633f 10 // 11 // Raw coverage files can be obtained either from /rawcover manager HTTP handler, 12 // or from syz-execprog with -coverfile flag. 13 // 14 // Usage: 15 // 16 // syz-cover -config config_file rawcover.file* 17 // 18 // or use all pcs in rg.Symbols 19 // 20 // syz-cover -config config_file 21 package main 22 23 import ( 24 "bufio" 25 "bytes" 26 "context" 27 "encoding/json" 28 "flag" 29 "fmt" 30 "io" 31 "os" 32 "os/exec" 33 "strconv" 34 "strings" 35 "time" 36 37 "cloud.google.com/go/civil" 38 "github.com/google/syzkaller/pkg/cover" 39 "github.com/google/syzkaller/pkg/cover/backend" 40 "github.com/google/syzkaller/pkg/coveragedb" 41 "github.com/google/syzkaller/pkg/covermerger" 42 "github.com/google/syzkaller/pkg/log" 43 "github.com/google/syzkaller/pkg/mgrconfig" 44 "github.com/google/syzkaller/pkg/osutil" 45 "github.com/google/syzkaller/pkg/tool" 46 "github.com/google/syzkaller/pkg/vminfo" 47 ) 48 49 var ( 50 flagConfig = flag.String("config", "", "configuration file") 51 flagModules = flag.String("modules", "", 52 "modules JSON info obtained from /modules (optional)") 53 flagPeriod = flag.String("period", "day", "time period(day[default], month, quarter)") 54 flagDateTo = flag.String("to", 55 civil.DateOf(time.Now()).String(), "heatmap date to(optional)") 56 flagForFile = flag.String("for-file", "", "[optional]show file coverage") 57 flagRepo = flag.String("repo", "git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git", 58 "[optional] repo to be used by -for-file") 59 flagCommit = flag.String("commit", "latest", "[optional] commit to be used by -for-file") 60 flagNamespace = flag.String("namespace", "upstream", "[optional] used by -for-file") 61 flagDebug = flag.Bool("debug", false, "[optional] enables detailed output") 62 flagSourceCommit = flag.String("source-commit", "", "[optional] filter input commit") 63 flagExports = flag.String("exports", "cover", 64 "[optional] comma separated list of exports for which we want to generate coverage, "+ 65 "possible values are: cover, subsystem, module, funccover, json, jsonl, rawcover, rawcoverfiles, all") 66 flagForce = flag.Bool("force", false, "[optional] create coverage report when "+ 67 "there are missing coverage callbacks") 68 ) 69 70 func toolFileCover() { 71 dateTo, err := civil.ParseDate(*flagDateTo) 72 if err != nil { 73 tool.Failf("failed to parse date from: %v", err) 74 } 75 tp, err := coveragedb.MakeTimePeriod(dateTo, *flagPeriod) 76 if err != nil { 77 tool.Fail(err) 78 } 79 config := cover.DefaultTextRenderConfig() 80 config.ShowLineSourceExplanation = *flagDebug 81 mr, err := cover.GetMergeResult(context.Background(), 82 *flagNamespace, 83 *flagRepo, 84 *flagCommit, 85 *flagSourceCommit, 86 *flagForFile, 87 nil, tp) 88 if err != nil { 89 tool.Fail(err) 90 } 91 92 details, err := cover.RendFileCoverage( 93 *flagRepo, 94 *flagCommit, 95 *flagForFile, 96 covermerger.MakeWebGit(nil), // get files directly from WebGits 97 mr, 98 config, 99 ) 100 if err != nil { 101 tool.Fail(err) 102 } 103 fmt.Println(details) 104 } 105 106 func initModules(cfg *mgrconfig.Config) []*vminfo.KernelModule { 107 modules, err := backend.DiscoverModules(cfg.SysTarget, cfg.KernelObj, cfg.ModuleObj) 108 if err != nil { 109 tool.Fail(err) 110 } 111 if *flagModules != "" { 112 m, err := loadModules(*flagModules) 113 if err != nil { 114 tool.Fail(err) 115 } 116 modules = m 117 } 118 return modules 119 } 120 121 func main() { 122 defer tool.Init()() 123 if *flagForFile != "" { 124 toolFileCover() 125 return 126 } 127 cfg, err := mgrconfig.LoadFile(*flagConfig) 128 if err != nil { 129 tool.Fail(err) 130 } 131 modules := initModules(cfg) 132 rg, err := cover.MakeReportGenerator(cfg, modules) 133 if err != nil { 134 tool.Fail(err) 135 } 136 pcs := initPCs(rg) 137 progs := []cover.Prog{{PCs: pcs}} 138 params := cover.HandlerParams{ 139 Progs: progs, 140 Debug: *flagDebug, 141 Force: *flagForce, 142 } 143 144 if *flagExports == "all" { 145 *flagExports = "cover,subsystem,module,funccover,rawcover,rawcoverfiles" 146 } 147 exports := strings.Split(*flagExports, ",") 148 for _, export := range exports { 149 log.Logf(1, "start generate %v", export) 150 switch export { 151 case "cover": 152 doReport(params, "syz-cover.html", rg.DoHTML) 153 case "subsystem": 154 doReport(params, "syz-cover-subsystem.html", rg.DoSubsystemCover) 155 case "module": 156 doReport(params, "syz-cover-module.html", rg.DoModuleCover) 157 case "funccover": 158 doReport(params, "syz-cover-funccover.csv", rg.DoFuncCover) 159 case "rawcover": 160 doReport(params, "rawcoverpcs", rg.DoRawCover) 161 case "rawcoverfiles": 162 doReport(params, "rawcoverfiles", rg.DoRawCoverFiles) 163 case "json": 164 doReport(params, "json", rg.DoLineJSON) 165 case "jsonl": 166 doReport(params, "jsonl", rg.DoCoverJSONL) 167 default: 168 tool.Failf("unknown export type: %q", export) 169 } 170 } 171 } 172 173 func doReport(params cover.HandlerParams, fname string, 174 fn func(w io.Writer, params cover.HandlerParams) error) { 175 buf := new(bytes.Buffer) 176 if err := fn(buf, params); err != nil { 177 tool.Fail(err) 178 } 179 log.Logf(0, "write to %v", fname) 180 if err := osutil.WriteFile(fname, buf.Bytes()); err != nil { 181 tool.Fail(err) 182 } 183 exec.Command("xdg-open", fname).Start() 184 } 185 186 func initPCs(rg *cover.ReportGenerator) []uint64 { 187 var pcs []uint64 188 if len(flag.Args()) == 0 { 189 pcs = rg.CallbackPoints 190 return pcs 191 } 192 pcs, err := readPCs(flag.Args()) 193 if err != nil { 194 tool.Fail(err) 195 } 196 return pcs 197 } 198 199 func readPCs(files []string) ([]uint64, error) { 200 var pcs []uint64 201 for _, file := range files { 202 data, err := os.ReadFile(file) 203 if err != nil { 204 return nil, err 205 } 206 for s := bufio.NewScanner(bytes.NewReader(data)); s.Scan(); { 207 line := strings.TrimSpace(s.Text()) 208 if line == "" { 209 continue 210 } 211 pc, err := strconv.ParseUint(line, 0, 64) 212 if err != nil { 213 return nil, err 214 } 215 pcs = append(pcs, pc) 216 } 217 } 218 return pcs, nil 219 } 220 221 func loadModules(fname string) ([]*vminfo.KernelModule, error) { 222 data, err := os.ReadFile(fname) 223 if err != nil { 224 return nil, err 225 } 226 var modules []*vminfo.KernelModule 227 err = json.Unmarshal(data, &modules) 228 if err != nil { 229 return nil, err 230 } 231 return modules, nil 232 }