Skip to content

Commit

Permalink
info: expose more prog jited info
Browse files Browse the repository at this point in the history
Expose these prog jited info:

1. JITed machine native instructions torvalds/linux@1e2709769086 ("bpf: Add BPF_OBJ_GET_INFO_BY_FD")
2. JITed line info torvalds/linux@c454a46b5efd ("bpf: Add bpf_line_info support")
3. JITed image lengths torvalds/linux@815581c11cc2 ("bpf: get JITed image lengths of functions via syscall")

Signed-off-by: Leon Hwang <[email protected]>
  • Loading branch information
Asphaltt committed Oct 24, 2024
1 parent d3c63ab commit 8c0466b
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 53 deletions.
48 changes: 28 additions & 20 deletions btf/ext_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,10 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er
return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil
}

type funcInfoMeta struct{}
type coreRelocationMeta struct{}
type (
funcInfoMeta struct{}
coreRelocationMeta struct{}
)

// Assign per-section metadata from BTF to a section's instructions.
func (ei *ExtInfos) Assign(insns asm.Instructions, section string) {
Expand All @@ -128,8 +130,8 @@ func AssignMetadataToInstructions(
funcInfos = funcInfos[1:]
}

if len(lineInfos.infos) > 0 && lineInfos.infos[0].offset == iter.Offset {
*iter.Ins = iter.Ins.WithSource(lineInfos.infos[0].line)
if len(lineInfos.infos) > 0 && lineInfos.infos[0].Offset == iter.Offset {
*iter.Ins = iter.Ins.WithSource(lineInfos.infos[0].Line)
lineInfos.infos = lineInfos.infos[1:]
}

Expand Down Expand Up @@ -178,9 +180,9 @@ marshal:
}
}

li := &lineInfo{
line: line,
offset: iter.Offset,
li := &LineInfo{
Line: line,
Offset: iter.Offset,
}
if err := li.marshal(&liBuf, b); err != nil {
return nil, nil, fmt.Errorf("write line info: %w", err)
Expand Down Expand Up @@ -518,12 +520,18 @@ func (li *Line) String() string {

// LineInfos contains a sorted list of line infos.
type LineInfos struct {
infos []lineInfo
infos []LineInfo
}

type lineInfo struct {
line *Line
offset asm.RawInstructionOffset
// Lines returns the sorted list of line infos.
func (li *LineInfos) Lines() []LineInfo {
return li.infos
}

// LineInfo represents a line info and its raw instruction offset.
type LineInfo struct {
Line *Line
Offset asm.RawInstructionOffset
}

// Constants for the format of bpfLineInfo.LineCol.
Expand Down Expand Up @@ -557,21 +565,21 @@ func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec
return newLineInfos(lis, spec.strings)
}

func newLineInfo(li bpfLineInfo, strings *stringTable) (lineInfo, error) {
func newLineInfo(li bpfLineInfo, strings *stringTable) (LineInfo, error) {
line, err := strings.Lookup(li.LineOff)
if err != nil {
return lineInfo{}, fmt.Errorf("lookup of line: %w", err)
return LineInfo{}, fmt.Errorf("lookup of line: %w", err)
}

fileName, err := strings.Lookup(li.FileNameOff)
if err != nil {
return lineInfo{}, fmt.Errorf("lookup of filename: %w", err)
return LineInfo{}, fmt.Errorf("lookup of filename: %w", err)
}

lineNumber := li.LineCol >> bpfLineShift
lineColumn := li.LineCol & bpfColumnMax

return lineInfo{
return LineInfo{
&Line{
fileName,
line,
Expand All @@ -584,7 +592,7 @@ func newLineInfo(li bpfLineInfo, strings *stringTable) (lineInfo, error) {

func newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineInfos, error) {
lis := LineInfos{
infos: make([]lineInfo, 0, len(blis)),
infos: make([]LineInfo, 0, len(blis)),
}
for _, bli := range blis {
li, err := newLineInfo(bli, strings)
Expand All @@ -594,14 +602,14 @@ func newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineInfos, error) {
lis.infos = append(lis.infos, li)
}
sort.Slice(lis.infos, func(i, j int) bool {
return lis.infos[i].offset <= lis.infos[j].offset
return lis.infos[i].Offset <= lis.infos[j].Offset
})
return lis, nil
}

// marshal writes the binary representation of the LineInfo to w.
func (li *lineInfo) marshal(w *bytes.Buffer, b *Builder) error {
line := li.line
func (li *LineInfo) marshal(w *bytes.Buffer, b *Builder) error {
line := li.Line
if line.lineNumber > bpfLineMax {
return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax)
}
Expand All @@ -621,7 +629,7 @@ func (li *lineInfo) marshal(w *bytes.Buffer, b *Builder) error {
}

bli := bpfLineInfo{
uint32(li.offset),
uint32(li.Offset),
fileNameOff,
lineOff,
(line.lineNumber << bpfLineShift) | line.lineColumn,
Expand Down
157 changes: 127 additions & 30 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io"
"os"
"reflect"
"slices"
"strings"
"syscall"
"time"
Expand Down Expand Up @@ -179,6 +180,46 @@ type programStats struct {
recursionMisses uint64
}

// ProgramJitedInfo holds information about JITed info of a program.
type ProgramJitedInfo struct {
// Ksyms holds the ksym addresses of the BPF program, including those of its
// subprograms.
//
// Available from 4.18.
Ksyms []uintptr

// Insns holds the JITed machine native instructions of the program,
// including those of its subprograms.
//
// Available from 4.13.
Insns []byte

// LineInfos holds the JITed line infos, which are kernel addresses.
//
// Available from 5.0.
LineInfos []uint64

// LineInfoRecSize is the size of a single line info record.
//
// Available from 5.0.
LineInfoRecSize uint32

// FuncLens holds the insns length of each function.
//
// Available from 4.18.
FuncLens []uint32
}

func (pji *ProgramJitedInfo) clone() *ProgramJitedInfo {
return &ProgramJitedInfo{
Ksyms: slices.Clone(pji.Ksyms),
Insns: slices.Clone(pji.Insns),
LineInfos: slices.Clone(pji.LineInfos),
LineInfoRecSize: pji.LineInfoRecSize,
FuncLens: slices.Clone(pji.FuncLens),
}
}

// ProgramInfo describes a program.
type ProgramInfo struct {
Type ProgramType
Expand All @@ -199,12 +240,12 @@ type ProgramInfo struct {
jitedSize uint32
verifiedInstructions uint32

jitedInfo ProgramJitedInfo

lineInfos []byte
numLineInfos uint32
funcInfos []byte
numFuncInfos uint32
ksymInfos []uint64
numKsymInfos uint32
}

func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
Expand Down Expand Up @@ -282,11 +323,33 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
makeSecondCall = true
}

pi.jitedInfo.LineInfoRecSize = info.JitedLineInfoRecSize
if info.JitedProgLen > 0 {
pi.jitedInfo.Insns = make([]byte, info.JitedProgLen)
info2.JitedProgLen = info.JitedProgLen
info2.JitedProgInsns = sys.NewSlicePointer(pi.jitedInfo.Insns)
makeSecondCall = true
}

if info.NrJitedFuncLens > 0 {
pi.jitedInfo.FuncLens = make([]uint32, info.NrJitedFuncLens)
info2.NrJitedFuncLens = info.NrJitedFuncLens
info2.JitedFuncLens = sys.NewSlicePointer(pi.jitedInfo.FuncLens)
makeSecondCall = true
}

if info.NrJitedLineInfo > 0 {
pi.jitedInfo.LineInfos = make([]uint64, info.NrJitedLineInfo)
info2.NrJitedLineInfo = info.NrJitedLineInfo
info2.JitedLineInfo = sys.NewSlicePointer(pi.jitedInfo.LineInfos)
info2.JitedLineInfoRecSize = info.JitedLineInfoRecSize
makeSecondCall = true
}

if info.NrJitedKsyms > 0 {
pi.ksymInfos = make([]uint64, info.NrJitedKsyms)
info2.JitedKsyms = sys.NewSlicePointer(pi.ksymInfos)
pi.jitedInfo.Ksyms = make([]uintptr, info.NrJitedKsyms)
info2.JitedKsyms = sys.NewSlicePointer(pi.jitedInfo.Ksyms)
info2.NrJitedKsyms = info.NrJitedKsyms
pi.numKsymInfos = info.NrJitedKsyms
makeSecondCall = true
}

Expand Down Expand Up @@ -380,6 +443,47 @@ func (pi *ProgramInfo) RecursionMisses() (uint64, bool) {
return 0, false
}

// withBTFSpec calls the provided callback with the BTF spec of the program.
//
// The base argument is used to resolve type IDs in the prog's BTF spec.
func (pi *ProgramInfo) withBTFSpec(base *btf.Spec, cb func(*btf.Spec) error) error {
id, ok := pi.BTFID()
if pi.numFuncInfos == 0 || !ok {
return fmt.Errorf("program created without BTF or unsupported kernel: %w", ErrNotSupported)
}

h, err := btf.NewHandleFromID(id)
if err != nil {
return fmt.Errorf("get BTF handle: %w", err)
}
defer h.Close()

spec, err := h.Spec(base)
if err != nil {
return fmt.Errorf("get BTF spec: %w", err)
}

return cb(spec)
}

// LineInfos returns the BTF line information of the program.
func (pi *ProgramInfo) LineInfos() (btf.LineInfos, error) {
var li btf.LineInfos

err := pi.withBTFSpec(nil, func(spec *btf.Spec) error {
var err error
li, err = btf.LoadLineInfos(
bytes.NewReader(pi.lineInfos),
internal.NativeEndian,
pi.numLineInfos,
spec,
)
return err
})

return li, err
}

// Instructions returns the 'xlated' instruction stream of the program
// after it has been verified and rewritten by the kernel. These instructions
// cannot be loaded back into the kernel as-is, this is mainly used for
Expand Down Expand Up @@ -524,11 +628,7 @@ func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) {
//
// The bool return value indicates whether this optional field is available.
func (pi *ProgramInfo) KsymAddrs() ([]uintptr, bool) {
addrs := make([]uintptr, 0, len(pi.ksymInfos))
for _, addr := range pi.ksymInfos {
addrs = append(addrs, uintptr(addr))
}
return addrs, pi.numKsymInfos > 0
return pi.jitedInfo.Ksyms, len(pi.jitedInfo.Ksyms) > 0
}

// FuncInfos returns the offset and function information of all (sub)programs in
Expand All @@ -540,28 +640,25 @@ func (pi *ProgramInfo) KsymAddrs() ([]uintptr, bool) {
// ErrNotSupported if the program was created without BTF or if the kernel
// doesn't support the field.
func (pi *ProgramInfo) FuncInfos() (btf.FuncOffsets, error) {
id, ok := pi.BTFID()
if pi.numFuncInfos == 0 || !ok {
return nil, fmt.Errorf("program created without BTF or unsupported kernel: %w", ErrNotSupported)
}

h, err := btf.NewHandleFromID(id)
if err != nil {
return nil, fmt.Errorf("get BTF handle: %w", err)
}
defer h.Close()
var funcs btf.FuncOffsets

err := pi.withBTFSpec(nil, func(spec *btf.Spec) error {
var err error
funcs, err = btf.LoadFuncInfos(
bytes.NewReader(pi.funcInfos),
internal.NativeEndian,
pi.numFuncInfos,
spec,
)
return err
})

spec, err := h.Spec(nil)
if err != nil {
return nil, fmt.Errorf("get BTF spec: %w", err)
}
return funcs, err
}

return btf.LoadFuncInfos(
bytes.NewReader(pi.funcInfos),
internal.NativeEndian,
pi.numFuncInfos,
spec,
)
// JitedInfo returns the JITed information of the program.
func (pi *ProgramInfo) JitedInfo() *ProgramJitedInfo {
return pi.jitedInfo.clone()
}

func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {
Expand Down
3 changes: 3 additions & 0 deletions internal/cmd/gentypes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,13 @@ import (
"ProgInfo", "bpf_prog_info",
[]patch{
replace(objName, "name"),
replace(pointer, "jited_prog_insns"),
replace(pointer, "xlated_prog_insns"),
replace(pointer, "map_ids"),
replace(pointer, "line_info"),
replace(pointer, "jited_line_info"),
replace(pointer, "jited_ksyms"),
replace(pointer, "jited_func_lens"),
replace(pointer, "func_info"),
replace(btfID, "btf_id", "attach_btf_obj_id"),
replace(typeID, "attach_btf_id"),
Expand Down
6 changes: 3 additions & 3 deletions internal/sys/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ type ProgInfo struct {
Tag [8]uint8
JitedProgLen uint32
XlatedProgLen uint32
JitedProgInsns uint64
JitedProgInsns Pointer
XlatedProgInsns Pointer
LoadTime uint64
CreatedByUid uint32
Expand All @@ -734,14 +734,14 @@ type ProgInfo struct {
NrJitedKsyms uint32
NrJitedFuncLens uint32
JitedKsyms Pointer
JitedFuncLens uint64
JitedFuncLens Pointer
BtfId BTFID
FuncInfoRecSize uint32
FuncInfo Pointer
NrFuncInfo uint32
NrLineInfo uint32
LineInfo Pointer
JitedLineInfo uint64
JitedLineInfo Pointer
NrJitedLineInfo uint32
LineInfoRecSize uint32
JitedLineInfoRecSize uint32
Expand Down

0 comments on commit 8c0466b

Please sign in to comment.