github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/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  	"encoding/json"
    27  	"flag"
    28  	"os"
    29  	"os/exec"
    30  	"strconv"
    31  	"strings"
    32  
    33  	"github.com/google/syzkaller/pkg/cover"
    34  	"github.com/google/syzkaller/pkg/mgrconfig"
    35  	"github.com/google/syzkaller/pkg/osutil"
    36  	"github.com/google/syzkaller/pkg/tool"
    37  )
    38  
    39  func main() {
    40  	var (
    41  		flagConfig  = flag.String("config", "", "configuration file")
    42  		flagModules = flag.String("modules", "",
    43  			"modules JSON info obtained from /modules (optional)")
    44  		flagExportCSV      = flag.String("csv", "", "export coverage data in csv format (optional)")
    45  		flagExportLineJSON = flag.String("json", "", "export coverage data with source line info in json format (optional)")
    46  		flagExportJSONL    = flag.String("jsonl", "", "export jsonl coverage data (optional)")
    47  		flagExportHTML     = flag.String("html", "", "save coverage HTML report to file (optional)")
    48  	)
    49  	defer tool.Init()()
    50  
    51  	cfg, err := mgrconfig.LoadFile(*flagConfig)
    52  	if err != nil {
    53  		tool.Fail(err)
    54  	}
    55  	var modules []cover.KernelModule
    56  	if *flagModules != "" {
    57  		m, err := loadModules(*flagModules)
    58  		if err != nil {
    59  			tool.Fail(err)
    60  		}
    61  		modules = m
    62  	}
    63  	rg, err := cover.MakeReportGenerator(cfg, cfg.KernelSubsystem, modules, false)
    64  	if err != nil {
    65  		tool.Fail(err)
    66  	}
    67  	var pcs []uint64
    68  	if len(flag.Args()) == 0 {
    69  		for _, s := range rg.Symbols {
    70  			pcs = append(pcs, s.PCs...)
    71  		}
    72  	} else {
    73  		pcs, err = readPCs(flag.Args())
    74  		if err != nil {
    75  			tool.Fail(err)
    76  		}
    77  	}
    78  	progs := []cover.Prog{{PCs: pcs}}
    79  	buf := new(bytes.Buffer)
    80  	params := cover.CoverHandlerParams{
    81  		Progs:       progs,
    82  		CoverFilter: nil,
    83  		Debug:       false,
    84  		Force:       false,
    85  	}
    86  	if *flagExportCSV != "" {
    87  		if err := rg.DoCSV(buf, params); err != nil {
    88  			tool.Fail(err)
    89  		}
    90  		if err := osutil.WriteFile(*flagExportCSV, buf.Bytes()); err != nil {
    91  			tool.Fail(err)
    92  		}
    93  		return
    94  	}
    95  	if *flagExportLineJSON != "" {
    96  		if err := rg.DoLineJSON(buf, params); err != nil {
    97  			tool.Fail(err)
    98  		}
    99  		if err := osutil.WriteFile(*flagExportLineJSON, buf.Bytes()); err != nil {
   100  			tool.Fail(err)
   101  		}
   102  		return
   103  	}
   104  	if *flagExportJSONL != "" {
   105  		if err := rg.DoCoverJSONL(buf, params); err != nil {
   106  			tool.Fail(err)
   107  		}
   108  		if err := osutil.WriteFile(*flagExportJSONL, buf.Bytes()); err != nil {
   109  			tool.Fail(err)
   110  		}
   111  		return
   112  	}
   113  	if err := rg.DoHTML(buf, params); err != nil {
   114  		tool.Fail(err)
   115  	}
   116  	if *flagExportHTML != "" {
   117  		if err := osutil.WriteFile(*flagExportHTML, buf.Bytes()); err != nil {
   118  			tool.Fail(err)
   119  		}
   120  		return
   121  	}
   122  	fn, err := osutil.TempFile("syz-cover")
   123  	if err != nil {
   124  		tool.Fail(err)
   125  	}
   126  	fn += ".html"
   127  	if err := osutil.WriteFile(fn, buf.Bytes()); err != nil {
   128  		tool.Fail(err)
   129  	}
   130  	if err := exec.Command("xdg-open", fn).Start(); err != nil {
   131  		tool.Failf("failed to start browser: %v", err)
   132  	}
   133  }
   134  
   135  func readPCs(files []string) ([]uint64, error) {
   136  	var pcs []uint64
   137  	for _, file := range files {
   138  		data, err := os.ReadFile(file)
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  		for s := bufio.NewScanner(bytes.NewReader(data)); s.Scan(); {
   143  			line := strings.TrimSpace(s.Text())
   144  			if line == "" {
   145  				continue
   146  			}
   147  			pc, err := strconv.ParseUint(line, 0, 64)
   148  			if err != nil {
   149  				return nil, err
   150  			}
   151  			pcs = append(pcs, pc)
   152  		}
   153  	}
   154  	return pcs, nil
   155  }
   156  
   157  func loadModules(fname string) ([]cover.KernelModule, error) {
   158  	data, err := os.ReadFile(fname)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	var modules []cover.KernelModule
   163  	err = json.Unmarshal(data, &modules)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	return modules, nil
   168  }