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