github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/tools/syz-query-subsystems/generator.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  package main
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"go/format"
    10  	"regexp"
    11  	"sort"
    12  	"strings"
    13  	"text/template"
    14  	"unicode"
    15  
    16  	"github.com/google/syzkaller/pkg/serializer"
    17  	"github.com/google/syzkaller/pkg/subsystem"
    18  	"github.com/google/syzkaller/pkg/vcs"
    19  )
    20  
    21  func generateSubsystemsFile(name string, list []*subsystem.Subsystem, commit *vcs.Commit) ([]byte, error) {
    22  	// Set names first -- we'll need them for filling in the Parents array.
    23  	objToName := map[*subsystem.Subsystem]string{}
    24  	for _, entry := range list {
    25  		varName := getVarName(entry)
    26  		if varName == "" {
    27  			return nil, fmt.Errorf("failed to get a var name for %#v", entry.Name)
    28  		}
    29  		objToName[entry] = varName
    30  	}
    31  
    32  	// Prepare the template data.
    33  	vars := &templateVars{
    34  		Name:       name,
    35  		CommitInfo: fmt.Sprintf(`Commit %s, "%.32s"`, commit.Hash, commit.Title),
    36  		Version:    commit.Date.Year()*10000 + int(commit.Date.Month())*100 + commit.Date.Day(),
    37  		Hierarchy:  hierarchyList(list),
    38  	}
    39  	for _, entry := range list {
    40  		varName := objToName[entry]
    41  		// The serializer does not understand parent references and just prints all the
    42  		// nested structures.
    43  		// Therefore we call it separately for the fields it can understand.
    44  		parents := []string{}
    45  		for _, p := range entry.Parents {
    46  			parents = append(parents, objToName[p])
    47  		}
    48  		sort.Strings(parents)
    49  		subsystem := &templateSubsystem{
    50  			VarName:      varName,
    51  			Name:         serializer.WriteString(entry.Name),
    52  			PathRules:    serializer.WriteString(entry.PathRules),
    53  			Parents:      parents,
    54  			NoReminders:  entry.NoReminders,
    55  			NoIndirectCc: entry.NoIndirectCc,
    56  		}
    57  		// Some of the records are mostly empty.
    58  		if len(entry.Maintainers) > 0 {
    59  			sort.Strings(entry.Maintainers)
    60  			subsystem.Maintainers = serializer.WriteString(entry.Maintainers)
    61  		}
    62  		if len(entry.Syscalls) > 0 {
    63  			subsystem.Syscalls = serializer.WriteString(entry.Syscalls)
    64  		}
    65  		if len(entry.Lists) > 0 {
    66  			subsystem.Lists = serializer.WriteString(entry.Lists)
    67  		}
    68  		vars.List = append(vars.List, subsystem)
    69  	}
    70  	tmpl, err := template.New("source").Parse(fileTemplate)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	var b bytes.Buffer
    75  	if err = tmpl.Execute(&b, vars); err != nil {
    76  		return nil, err
    77  	}
    78  	return format.Source(b.Bytes())
    79  }
    80  
    81  func getVarName(entry *subsystem.Subsystem) string {
    82  	varName := makeVarRegexp.ReplaceAllString(strings.ToLower(entry.Name), "")
    83  	if varName == "" {
    84  		return ""
    85  	}
    86  	if unicode.IsDigit(rune(varName[0])) {
    87  		return "_" + varName
    88  	}
    89  	return varName
    90  }
    91  
    92  func hierarchyList(list []*subsystem.Subsystem) []string {
    93  	children := map[*subsystem.Subsystem][]*subsystem.Subsystem{}
    94  	for _, entry := range list {
    95  		for _, p := range entry.Parents {
    96  			children[p] = append(children[p], entry)
    97  		}
    98  	}
    99  	ret := []string{}
   100  	var dfs func(*subsystem.Subsystem, string)
   101  	dfs = func(entry *subsystem.Subsystem, prefix string) {
   102  		ret = append(ret, fmt.Sprintf("%s- %s", prefix, entry.Name))
   103  		for _, child := range children[entry] {
   104  			dfs(child, prefix+"  ")
   105  		}
   106  	}
   107  	for _, entity := range list {
   108  		if len(entity.Parents) == 0 {
   109  			dfs(entity, "")
   110  		}
   111  	}
   112  	return ret
   113  }
   114  
   115  var makeVarRegexp = regexp.MustCompile(`[^\w]|^([^a-z0-9]+)`)
   116  
   117  type templateSubsystem struct {
   118  	VarName      string
   119  	Name         string
   120  	Syscalls     string
   121  	PathRules    string
   122  	Lists        string
   123  	Maintainers  string
   124  	Parents      []string
   125  	NoReminders  bool
   126  	NoIndirectCc bool
   127  }
   128  
   129  type templateVars struct {
   130  	Name       string
   131  	Version    int
   132  	CommitInfo string
   133  	List       []*templateSubsystem
   134  	Hierarchy  []string
   135  }
   136  
   137  const fileTemplate = `// Code generated by the syz-query-subsystems tool. DO NOT EDIT.
   138  {{- if .CommitInfo}}
   139  // {{.CommitInfo}}
   140  {{- end}}
   141  
   142  package lists
   143  
   144  import . "github.com/google/syzkaller/pkg/subsystem"
   145  
   146  func init() {
   147    RegisterList("{{.Name}}", subsystems_{{.Name}}(), {{.Version}})
   148  }
   149  
   150  // The subsystem list:
   151  {{- range .Hierarchy}}
   152  // {{.}}
   153  {{- end}}
   154  
   155  func subsystems_{{.Name}}() []*Subsystem{
   156  var {{range $i, $item := .List}}
   157  {{- if gt $i 0}}, {{end}}
   158  {{- .VarName}}
   159  {{- end}} Subsystem
   160  
   161  {{range .List}}
   162  {{.VarName}} = Subsystem{
   163   Name: {{.Name}},
   164  {{- if .Syscalls}}
   165   Syscalls: {{.Syscalls}},
   166  {{- end}}
   167  {{- if .Lists}}
   168   Lists: {{.Lists}},
   169  {{- end}}
   170  {{- if .Maintainers}}
   171   Maintainers: {{.Maintainers}},
   172  {{- end}}
   173  {{- if .Parents}}
   174   Parents: []*Subsystem{ {{range .Parents}} &{{.}}, {{end}} },
   175  {{- end}}
   176   PathRules: {{.PathRules}},
   177  {{- if .NoReminders}}
   178   NoReminders: true,
   179  {{- end}}
   180  {{- if .NoIndirectCc}}
   181  NoIndirectCc: true,
   182  {{- end}}
   183  }
   184  {{end}}
   185  
   186  return  []*Subsystem{
   187  {{range .List}} &{{.VarName}}, {{- end}}
   188  }
   189  
   190  }
   191  `