From 4ad10eb0cd82918694bef9a5d65e3ce18ae7dfda Mon Sep 17 00:00:00 2001 From: tyrone-wu Date: Thu, 5 Sep 2024 21:37:49 +0000 Subject: [PATCH] info: expose bpf_prog_info fields to ProgramInfo Exposes additional metadata fields from `bpf_prog_info` to `ProgramInfo`: - jited_prog_len - xlated_prog_len - load_time - verified_insns Signed-off-by: tyrone-wu --- info.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- info_test.go | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/info.go b/info.go index 04c60c64b..93cc2dbae 100644 --- a/info.go +++ b/info.go @@ -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 @@ -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), + verifiedInstructions: info.VerifiedInsns, } // Start with a clean struct for the second call, otherwise we may get EFAULT. @@ -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. @@ -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 { diff --git a/info_test.go b/info_test.go index 13ec3bb5c..a0987c4fd 100644 --- a/info_test.go +++ b/info_test.go @@ -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)) + } } }) }