Skip to content

Commit

Permalink
pkg/bpf: move maps memlock retrieval from bugtool to bpf
Browse files Browse the repository at this point in the history
These operations might eventually be taken care of by cilium/ebpf
directly and we can remove this. I moved this to the bpf package as it
made little sense to import pkg/bugtool to just perform those operations
on maps.

Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
  • Loading branch information
mtardy committed Oct 8, 2024
1 parent 3897867 commit 3f7f1f6
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 85 deletions.
100 changes: 100 additions & 0 deletions pkg/bpf/maps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

package bpf

import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"

"github.com/cilium/ebpf"
)

type ExtendedMapInfo struct {
ebpf.MapInfo
Memlock int
}

func ExtendedInfoFromMapID(id int) (ExtendedMapInfo, error) {
m, err := ebpf.NewMapFromID(ebpf.MapID(id))
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed creating a map FD from ID: %w", err)
}
defer m.Close()

xInfo, err := ExtendedInfoFromMap(m)
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed to retrieve extended info from map %v: %w", m, err)
}

return xInfo, nil
}

func ExtendedInfoFromMap(m *ebpf.Map) (ExtendedMapInfo, error) {
info, err := m.Info()
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed to retrieve map info: %w", err)
}

memlock, err := ParseMemlockFromFDInfo(m.FD())
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed to parse memlock from fd (%d) info: %w", m.FD(), err)
}
return ExtendedMapInfo{
MapInfo: *info,
Memlock: memlock,
}, nil
}

func MemlockInfoFromMapID(id int) (ExtendedMapInfo, error) {
m, err := ebpf.NewMapFromID(ebpf.MapID(id))
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed creating a map FD from ID: %w", err)
}
defer m.Close()
memlock, err := ParseMemlockFromFDInfo(m.FD())
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed parsing fdinfo for memlock: %w", err)
}
info, err := m.Info()
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed retrieving info from map: %w", err)
}

return ExtendedMapInfo{
MapInfo: *info,
Memlock: memlock,
}, nil
}

func ParseMemlockFromFDInfo(fd int) (int, error) {
path := fmt.Sprintf("/proc/self/fdinfo/%d", fd)
file, err := os.Open(path)
if err != nil {
return 0, fmt.Errorf("failed to open file %q: %w", path, err)
}
defer file.Close()
return parseMemlockFromFDInfoReader(file)
}

func parseMemlockFromFDInfoReader(r io.Reader) (int, error) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) > 1 && fields[0] == "memlock:" {
memlock, err := strconv.Atoi(fields[1])
if err != nil {
return 0, fmt.Errorf("failed converting memlock to int: %w", err)
}
return memlock, nil
}
}
if err := scanner.Err(); err != nil {
return 0, fmt.Errorf("failed to scan: %w", err)
}
return 0, fmt.Errorf("didn't find memlock field")
}
2 changes: 1 addition & 1 deletion pkg/bugtool/maps_test.go → pkg/bpf/maps_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

package bugtool
package bpf

import (
"io"
Expand Down
102 changes: 18 additions & 84 deletions pkg/bugtool/maps.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,21 @@
package bugtool

import (
"bufio"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"syscall"

"github.com/cilium/ebpf"
"github.com/cilium/tetragon/pkg/bpf"
"golang.org/x/exp/maps"
)

type ExtendedMapInfo struct {
ebpf.MapInfo
Memlock int
}

// TotalMemlockBytes iterates over the extend map info and sums the memlock field.
func TotalMemlockBytes(infos []ExtendedMapInfo) int {
func TotalMemlockBytes(infos []bpf.ExtendedMapInfo) int {
var sum int
for _, info := range infos {
sum += info.Memlock
Expand All @@ -37,14 +29,14 @@ func TotalMemlockBytes(infos []ExtendedMapInfo) int {
// FindMapsUsedByPinnedProgs returns all info of maps used by the prog pinned
// under the path specified as argument. It also retrieve all the maps
// referenced in progs referenced in program array maps (tail calls).
func FindMapsUsedByPinnedProgs(path string) ([]ExtendedMapInfo, error) {
func FindMapsUsedByPinnedProgs(path string) ([]bpf.ExtendedMapInfo, error) {
mapIDs, err := mapIDsFromPinnedProgs(path)
if err != nil {
return nil, fmt.Errorf("failed retrieving map IDs: %w", err)
}
mapInfos := []ExtendedMapInfo{}
mapInfos := []bpf.ExtendedMapInfo{}
for _, mapID := range mapIDs {
memlockInfo, err := memlockInfoFromMapID(mapID)
memlockInfo, err := bpf.ExtendedInfoFromMapID(mapID)
if err != nil {
return nil, fmt.Errorf("failed retrieving map memlock from ID: %w", err)
}
Expand All @@ -55,10 +47,10 @@ func FindMapsUsedByPinnedProgs(path string) ([]ExtendedMapInfo, error) {

// FindAllMaps iterates over all maps loaded on the host using MapGetNextID and
// parses fdinfo to look for memlock.
func FindAllMaps() ([]ExtendedMapInfo, error) {
func FindAllMaps() ([]bpf.ExtendedMapInfo, error) {
var mapID ebpf.MapID
var err error
mapInfos := []ExtendedMapInfo{}
mapInfos := []bpf.ExtendedMapInfo{}

for {
mapID, err = ebpf.MapGetNextID(mapID)
Expand All @@ -79,12 +71,12 @@ func FindAllMaps() ([]ExtendedMapInfo, error) {
return nil, fmt.Errorf("failed to retrieve map info: %w", err)
}

memlock, err := parseMemlockFromFDInfo(m.FD())
memlock, err := bpf.ParseMemlockFromFDInfo(m.FD())
if err != nil {
return nil, fmt.Errorf("failed parsing fdinfo to retrieve memlock: %w", err)
}

mapInfos = append(mapInfos, ExtendedMapInfo{
mapInfos = append(mapInfos, bpf.ExtendedMapInfo{
MapInfo: *info,
Memlock: memlock,
})
Expand All @@ -93,8 +85,8 @@ func FindAllMaps() ([]ExtendedMapInfo, error) {

// FindPinnedMaps returns all info of maps pinned under the path
// specified as argument.
func FindPinnedMaps(path string) ([]ExtendedMapInfo, error) {
var infos []ExtendedMapInfo
func FindPinnedMaps(path string) ([]bpf.ExtendedMapInfo, error) {
var infos []bpf.ExtendedMapInfo
err := filepath.WalkDir(path, func(path string, d fs.DirEntry, _ error) error {
if d.IsDir() {
return nil // skip directories
Expand All @@ -116,20 +108,11 @@ func FindPinnedMaps(path string) ([]ExtendedMapInfo, error) {
return nil // skip non map
}

info, err := m.Info()
if err != nil {
return fmt.Errorf("failed to retrieve map info: %w", err)
}

memlock, err := parseMemlockFromFDInfo(m.FD())
xInfo, err := bpf.ExtendedInfoFromMap(m)
if err != nil {
return fmt.Errorf("failed to parse memlock from fd (%d) info: %w", m.FD(), err)
return fmt.Errorf("failed to retrieve extended info from map %v: %w", m, err)
}

infos = append(infos, ExtendedMapInfo{
MapInfo: *info,
Memlock: memlock,
})
infos = append(infos, xInfo)
return nil
})
if err != nil {
Expand Down Expand Up @@ -257,55 +240,6 @@ func mapIDsFromPinnedProgs(path string) ([]int, error) {
return maps.Keys(mapSet), nil
}

func memlockInfoFromMapID(id int) (ExtendedMapInfo, error) {
m, err := ebpf.NewMapFromID(ebpf.MapID(id))
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed creating a map FD from ID: %w", err)
}
defer m.Close()
memlock, err := parseMemlockFromFDInfo(m.FD())
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed parsing fdinfo for memlock: %w", err)
}
info, err := m.Info()
if err != nil {
return ExtendedMapInfo{}, fmt.Errorf("failed retrieving info from map: %w", err)
}

return ExtendedMapInfo{
MapInfo: *info,
Memlock: memlock,
}, nil
}

func parseMemlockFromFDInfo(fd int) (int, error) {
path := fmt.Sprintf("/proc/self/fdinfo/%d", fd)
file, err := os.Open(path)
if err != nil {
return 0, fmt.Errorf("failed to open file %q: %w", path, err)
}
defer file.Close()
return parseMemlockFromFDInfoReader(file)
}

func parseMemlockFromFDInfoReader(r io.Reader) (int, error) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) > 1 && fields[0] == "memlock:" {
memlock, err := strconv.Atoi(fields[1])
if err != nil {
return 0, fmt.Errorf("failed converting memlock to int: %w", err)
}
return memlock, nil
}
}
if err := scanner.Err(); err != nil {
return 0, fmt.Errorf("failed to scan: %w", err)
}
return 0, fmt.Errorf("didn't find memlock field")
}

func isProg(fd int) (bool, error) {
return isBPFObject("prog", fd)
}
Expand Down Expand Up @@ -395,7 +329,7 @@ func RunMapsChecks(path string) (*MapsChecksOutput, error) {
out.TotalMemlockBytes.PinnedMaps = TotalMemlockBytes(pinnedMaps)

// details on map distribution
pinnedProgsMapsSet := map[int]ExtendedMapInfo{}
pinnedProgsMapsSet := map[int]bpf.ExtendedMapInfo{}
for _, info := range pinnedProgsMaps {
id, ok := info.ID()
if !ok {
Expand All @@ -404,7 +338,7 @@ func RunMapsChecks(path string) (*MapsChecksOutput, error) {
pinnedProgsMapsSet[int(id)] = info
}

pinnedMapsSet := map[int]ExtendedMapInfo{}
pinnedMapsSet := map[int]bpf.ExtendedMapInfo{}
for _, info := range pinnedMaps {
id, ok := info.ID()
if !ok {
Expand Down Expand Up @@ -442,7 +376,7 @@ func RunMapsChecks(path string) (*MapsChecksOutput, error) {

// aggregates maps total memory use
aggregatedMapsSet := map[string]struct {
ExtendedMapInfo
bpf.ExtendedMapInfo
count int
}{}
var total int
Expand All @@ -454,7 +388,7 @@ func RunMapsChecks(path string) (*MapsChecksOutput, error) {
aggregatedMapsSet[m.Name] = e
} else {
aggregatedMapsSet[m.Name] = struct {
ExtendedMapInfo
bpf.ExtendedMapInfo
count int
}{m, 1}
}
Expand Down

0 comments on commit 3f7f1f6

Please sign in to comment.