Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

info: expose ksym info and func info by ProgramInfo #1576

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ type ProgramInfo struct {
numLineInfos uint32
funcInfos []byte
numFuncInfos uint32
ksymInfos []uint64
numKsymInfos uint32
}

func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
Expand Down Expand Up @@ -281,6 +283,14 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
makeSecondCall = true
}

if info.NrJitedKsyms > 0 {
pi.ksymInfos = make([]uint64, info.NrJitedKsyms)
info2.JitedKsyms = sys.NewPointer(unsafe.Pointer(&pi.ksymInfos[0]))
info2.NrJitedKsyms = info.NrJitedKsyms
pi.numKsymInfos = info.NrJitedKsyms
makeSecondCall = true
}

if makeSecondCall {
if err := sys.ObjInfo(fd, &info2); err != nil {
return nil, err
Expand Down Expand Up @@ -508,6 +518,34 @@ func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) {
return pi.verifiedInstructions, pi.verifiedInstructions > 0
}

// KsymAddrs returns the ksym addresses of bpf programs, including subprograms,
// of current bpf program. The addresses are same as the ones in /proc/kallsyms.
//
// Available from 4.18.
//
// The bool return value indicates whether this optional field is available.
func (pi *ProgramInfo) KsymAddrs() ([]uint64, bool) {
return pi.ksymInfos, pi.numKsymInfos > 0
}

type ProgramFunctionInfo = sys.FuncInfo

// FuncInfos returns the function information of bpf programs, including
// subprograms, of current bpf program.
//
// Available from 5.0.
//
// The bool return value indicates whether this optional field is available.
func (pi *ProgramInfo) FuncInfos() ([]ProgramFunctionInfo, bool) {
if pi.numFuncInfos == 0 {
return nil, false
}

ptr := (*ProgramFunctionInfo)(unsafe.Pointer(&pi.funcInfos[0]))
funcs := unsafe.Slice(ptr, int(pi.numFuncInfos))
return funcs, true
}

func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {
fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int()))
if err != nil {
Expand Down
113 changes: 113 additions & 0 deletions info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,3 +510,116 @@ func TestZero(t *testing.T) {
qt.Assert(t, qt.IsTrue(zero(&inul)))
qt.Assert(t, qt.IsFalse(zero(&ione)))
}

func TestProgInfoKsym(t *testing.T) {
testutils.SkipOnOldKernel(t, "4.18", "Program ksym addresses")

spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/loader-%s.elf"))
if err != nil {
t.Fatal(err)
}

var obj struct {
Main *Program `ebpf:"xdp_prog"`
}

err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.Main.Close()

info, err := obj.Main.Info()
if err != nil {
t.Fatal(err)
}

expectedNumKsymInfos := uint32(5)
if info.numKsymInfos != expectedNumKsymInfos {
t.Errorf("expected %d ksym infos, got %d", expectedNumKsymInfos, info.numKsymInfos)
}

for _, ksym := range info.ksymInfos {
if ksym == 0 {
t.Errorf("expected non-zero ksym address")
}
}
}

func TestProgInfoFuncInfo(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.0", "Program func info")

spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/loader-%s.elf"))
if err != nil {
t.Fatal(err)
}

var obj struct {
Main *Program `ebpf:"xdp_prog"`
}

err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.Main.Close()

info, err := obj.Main.Info()
if err != nil {
t.Fatal(err)
}

funcs, ok := info.FuncInfos()
if !ok {
t.Fatalf("expected function info to be available")
}

expectedNumFuncInfos := uint32(5)
if info.numFuncInfos != expectedNumFuncInfos {
t.Fatalf("expected %d ksym infos, got %d", expectedNumFuncInfos, info.numFuncInfos)
}

handle, err := obj.Main.Handle()
if err != nil {
t.Fatalf("failed to get program handle: %v", err)
}
defer handle.Close()

btfSpec, err := handle.Spec(nil)
if err != nil {
t.Errorf("failed to get BTF spec: %v", err)
return
}

expectedFuncInfo := map[string]bool{
"xdp_prog": false,
"static_fn": false,
"global_fn": false,
"global_fn2": false,
"global_fn3": false,
}

for _, funcInfo := range funcs {
btfInfo, err := btfSpec.TypeByID(sys.TypeID(funcInfo.TypeId))
if err != nil {
t.Errorf("failed to get BTF type info: %v", err)
return
}

funcBtfInfo, ok := btfInfo.(*btf.Func)
if !ok {
t.Errorf("expected BTF type to be a function, got %T", btfInfo)
return
}

expectedFuncInfo[funcBtfInfo.Name] = true
}

for fn, found := range expectedFuncInfo {
if !found {
t.Errorf("func %q not found", fn)
}
}
}
1 change: 1 addition & 0 deletions internal/cmd/gentypes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ import (
replace(pointer, "xlated_prog_insns"),
replace(pointer, "map_ids"),
replace(pointer, "line_info"),
replace(pointer, "jited_ksyms"),
replace(pointer, "func_info"),
replace(btfID, "btf_id", "attach_btf_obj_id"),
replace(typeID, "attach_btf_id"),
Expand Down
2 changes: 1 addition & 1 deletion internal/sys/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ type ProgInfo struct {
NetnsIno uint64
NrJitedKsyms uint32
NrJitedFuncLens uint32
JitedKsyms uint64
JitedKsyms Pointer
JitedFuncLens uint64
BtfId BTFID
FuncInfoRecSize uint32
Expand Down