Files
LearnGO/go/pkg/mod/github.com/cilium/ebpf@v0.11.0/cmd/bpf2go/output.go
T
2024-09-19 21:38:24 -04:00

245 lines
5.3 KiB
Go

package main
import (
"bytes"
_ "embed"
"fmt"
"go/build/constraint"
"go/token"
"io"
"sort"
"strings"
"text/template"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal"
)
//go:embed output.tpl
var commonRaw string
var commonTemplate = template.Must(template.New("common").Parse(commonRaw))
type templateName string
func (n templateName) maybeExport(str string) string {
if token.IsExported(string(n)) {
return toUpperFirst(str)
}
return str
}
func (n templateName) Bytes() string {
return "_" + toUpperFirst(string(n)) + "Bytes"
}
func (n templateName) Specs() string {
return string(n) + "Specs"
}
func (n templateName) ProgramSpecs() string {
return string(n) + "ProgramSpecs"
}
func (n templateName) MapSpecs() string {
return string(n) + "MapSpecs"
}
func (n templateName) Load() string {
return n.maybeExport("load" + toUpperFirst(string(n)))
}
func (n templateName) LoadObjects() string {
return n.maybeExport("load" + toUpperFirst(string(n)) + "Objects")
}
func (n templateName) Objects() string {
return string(n) + "Objects"
}
func (n templateName) Maps() string {
return string(n) + "Maps"
}
func (n templateName) Programs() string {
return string(n) + "Programs"
}
func (n templateName) CloseHelper() string {
return "_" + toUpperFirst(string(n)) + "Close"
}
type outputArgs struct {
// Package of the resulting file.
pkg string
// The prefix of all names declared at the top-level.
stem string
// Build constraints included in the resulting file.
constraints constraint.Expr
// Maps to be emitted.
maps []string
// Programs to be emitted.
programs []string
// Types to be emitted.
types []btf.Type
// Filename of the ELF object to embed.
obj string
out io.Writer
}
func output(args outputArgs) error {
maps := make(map[string]string)
for _, name := range args.maps {
maps[name] = internal.Identifier(name)
}
programs := make(map[string]string)
for _, name := range args.programs {
programs[name] = internal.Identifier(name)
}
typeNames := make(map[btf.Type]string)
for _, typ := range args.types {
typeNames[typ] = args.stem + internal.Identifier(typ.TypeName())
}
// Ensure we don't have conflicting names and generate a sorted list of
// named types so that the output is stable.
types, err := sortTypes(typeNames)
if err != nil {
return err
}
module := currentModule()
gf := &btf.GoFormatter{
Names: typeNames,
Identifier: internal.Identifier,
}
ctx := struct {
*btf.GoFormatter
Module string
Package string
Constraints constraint.Expr
Name templateName
Maps map[string]string
Programs map[string]string
Types []btf.Type
TypeNames map[btf.Type]string
File string
}{
gf,
module,
args.pkg,
args.constraints,
templateName(args.stem),
maps,
programs,
types,
typeNames,
args.obj,
}
var buf bytes.Buffer
if err := commonTemplate.Execute(&buf, &ctx); err != nil {
return fmt.Errorf("can't generate types: %s", err)
}
return internal.WriteFormatted(buf.Bytes(), args.out)
}
func collectFromSpec(spec *ebpf.CollectionSpec, cTypes []string, skipGlobalTypes bool) (maps, programs []string, types []btf.Type, _ error) {
for name := range spec.Maps {
// Skip .rodata, .data, .bss, etc. sections
if !strings.HasPrefix(name, ".") {
maps = append(maps, name)
}
}
for name := range spec.Programs {
programs = append(programs, name)
}
types, err := collectCTypes(spec.Types, cTypes)
if err != nil {
return nil, nil, nil, fmt.Errorf("collect C types: %w", err)
}
// Collect map key and value types, unless we've been asked not to.
if skipGlobalTypes {
return maps, programs, types, nil
}
for _, typ := range collectMapTypes(spec.Maps) {
switch btf.UnderlyingType(typ).(type) {
case *btf.Datasec:
// Avoid emitting .rodata, .bss, etc. for now. We might want to
// name these types differently, etc.
continue
case *btf.Int:
// Don't emit primitive types by default.
continue
}
types = append(types, typ)
}
return maps, programs, types, nil
}
func collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) {
var result []btf.Type
for _, cType := range names {
typ, err := types.AnyTypeByName(cType)
if err != nil {
return nil, err
}
result = append(result, typ)
}
return result, nil
}
// collectMapTypes returns a list of all types used as map keys or values.
func collectMapTypes(maps map[string]*ebpf.MapSpec) []btf.Type {
var result []btf.Type
for _, m := range maps {
if m.Key != nil && m.Key.TypeName() != "" {
result = append(result, m.Key)
}
if m.Value != nil && m.Value.TypeName() != "" {
result = append(result, m.Value)
}
}
return result
}
// sortTypes returns a list of types sorted by their (generated) Go type name.
//
// Duplicate Go type names are rejected.
func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) {
var types []btf.Type
var names []string
for typ, name := range typeNames {
i := sort.SearchStrings(names, name)
if i >= len(names) {
types = append(types, typ)
names = append(names, name)
continue
}
if names[i] == name {
return nil, fmt.Errorf("type name %q is used multiple times", name)
}
types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...)
names = append(names[:i], append([]string{name}, names[i:]...)...)
}
return types, nil
}