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 additional bpf_prog_info fields in ProgramInfo #1512

Merged
merged 1 commit into from
Sep 12, 2024
Merged
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
52 changes: 50 additions & 2 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,12 @@ type ProgramInfo struct {
haveCreatedByUID bool
btf btf.ID
stats *programStats
loadTime time.Duration

maps []MapID
insns []byte
maps []MapID
insns []byte
jitedSize uint32
verifiedInstructions uint32

lineInfos []byte
numLineInfos uint32
Expand Down Expand Up @@ -164,6 +167,9 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
runCount: info.RunCnt,
recursionMisses: info.RecursionMisses,
},
jitedSize: info.JitedProgLen,
loadTime: time.Duration(info.LoadTime),
florianl marked this conversation as resolved.
Show resolved Hide resolved
verifiedInstructions: info.VerifiedInsns,
}

// Start with a clean struct for the second call, otherwise we may get EFAULT.
Expand Down Expand Up @@ -391,6 +397,29 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
return insns, nil
}

// JitedSize returns the size of the program's JIT-compiled machine code in bytes, which is the
// actual code executed on the host's CPU. This field requires the BPF JIT compiler to be enabled.
//
// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.
func (pi *ProgramInfo) JitedSize() (uint32, error) {
if pi.jitedSize == 0 {
return 0, fmt.Errorf("insufficient permissions, unsupported kernel, or JIT compiler disabled: %w", ErrNotSupported)
}
return pi.jitedSize, nil
}

// TranslatedSize returns the size of the program's translated instructions in bytes, after it has
// been verified and rewritten by the kernel.
//
// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.
func (pi *ProgramInfo) TranslatedSize() (int, error) {
insns := len(pi.insns)
if insns == 0 {
return 0, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
}
return insns, nil
}

// MapIDs returns the maps related to the program.
//
// Available from 4.15.
Expand All @@ -400,6 +429,25 @@ func (pi *ProgramInfo) MapIDs() ([]MapID, bool) {
return pi.maps, pi.maps != nil
}

// LoadTime returns when the program was loaded since boot time.
//
// Available from 4.15.
//
// The bool return value indicates whether this optional field is available.
func (pi *ProgramInfo) LoadTime() (time.Duration, bool) {
// loadTime and NrMapIds were introduced in the same kernel version.
return pi.loadTime, pi.loadTime > 0
}

// VerifiedInstructions returns the number verified instructions in the program.
//
// Available from 5.16.
//
// The bool return value indicates whether this optional field is available.
func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) {
return pi.verifiedInstructions, pi.verifiedInstructions > 0
}

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
43 changes: 41 additions & 2 deletions info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,55 @@ func TestProgramInfo(t *testing.T) {
}

if name == "proc" {
_, err := info.JitedSize()
qt.Assert(t, qt.IsNotNil(err))

_, err = info.TranslatedSize()
qt.Assert(t, qt.IsNotNil(err))

_, ok := info.CreatedByUID()
qt.Assert(t, qt.IsFalse(ok))

_, ok = info.LoadTime()
qt.Assert(t, qt.IsFalse(ok))

_, ok = info.VerifiedInstructions()
qt.Assert(t, qt.IsFalse(ok))
} else {
uid, ok := info.CreatedByUID()
if testutils.IsKernelLessThan(t, "4.15") {
if jitedSize, err := info.JitedSize(); testutils.IsKernelLessThan(t, "4.13") {
qt.Assert(t, qt.IsNotNil(err))
} else {
qt.Assert(t, qt.IsNil(err))
qt.Assert(t, qt.IsTrue(jitedSize > 0))
}

if xlatedSize, err := info.TranslatedSize(); testutils.IsKernelLessThan(t, "4.13") {
qt.Assert(t, qt.IsNotNil(err))
} else {
qt.Assert(t, qt.IsNil(err))
qt.Assert(t, qt.IsTrue(xlatedSize > 0))
}

if uid, ok := info.CreatedByUID(); testutils.IsKernelLessThan(t, "4.15") {
qt.Assert(t, qt.IsFalse(ok))
} else {
qt.Assert(t, qt.IsTrue(ok))
qt.Assert(t, qt.Equals(uid, uint32(os.Getuid())))
}

if loadTime, ok := info.LoadTime(); testutils.IsKernelLessThan(t, "4.15") {
qt.Assert(t, qt.IsFalse(ok))
} else {
qt.Assert(t, qt.IsTrue(ok))
qt.Assert(t, qt.IsTrue(loadTime > 0))
}

if verifiedInsns, ok := info.VerifiedInstructions(); testutils.IsKernelLessThan(t, "5.16") {
qt.Assert(t, qt.IsFalse(ok))
} else {
qt.Assert(t, qt.IsTrue(ok))
qt.Assert(t, qt.IsTrue(verifiedInsns > 0))
}
}
})
}
Expand Down
Loading