github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/agent/ebpfspy/session_maps_linux.go (about)

     1  //go:build ebpfspy
     2  
     3  // Package ebpfspy provides integration with Linux eBPF. It is a rough copy of profile.py from BCC tools:
     4  //
     5  //	https://github.com/iovisor/bcc/blob/master/tools/profile.py
     6  package ebpfspy
     7  
     8  import (
     9  	"unsafe"
    10  )
    11  
    12  //#cgo CFLAGS: -I./bpf/
    13  //#include <linux/types.h>
    14  //#include "profile.bpf.h"
    15  import "C"
    16  
    17  func (s *Session) getCountsMapValues() (keys [][]byte, values [][]byte, batch bool, err error) {
    18  	// try lookup_and_delete_batch
    19  	var (
    20  		mapSize = C.PROFILE_MAPS_SIZE
    21  		keySize = int(unsafe.Sizeof(C.struct_profile_key_t{}))
    22  		allKeys = make([]byte, mapSize*keySize)
    23  		pKeys   = unsafe.Pointer(&allKeys[0])
    24  		nextKey = C.struct_profile_key_t{}
    25  	)
    26  	values, err = s.mapCounts.GetValueAndDeleteBatch(pKeys, nil, unsafe.Pointer(&nextKey), uint32(mapSize))
    27  	if len(values) > 0 {
    28  		keys = collectBatchValues(allKeys, len(values), keySize)
    29  		return keys, values, true, nil
    30  	}
    31  
    32  	// batch failed or unsupported or just unlucky and got 0 stack-traces
    33  	// try iterating
    34  	it := s.mapCounts.Iterator()
    35  	for it.Next() {
    36  		key := it.Key()
    37  		v, err := s.mapCounts.GetValue(unsafe.Pointer(&key[0]))
    38  		if err != nil {
    39  			return nil, nil, false, err
    40  		}
    41  		keyCopy := make([]byte, len(key)) // The slice is valid only until the next call to Next.
    42  		copy(keyCopy, key)
    43  		keys = append(keys, keyCopy)
    44  		values = append(values, v)
    45  	}
    46  	return keys, values, false, nil
    47  }
    48  
    49  func (s *Session) clearCountsMap(keys [][]byte, batch bool) error {
    50  	if len(keys) == 0 {
    51  		return nil
    52  	}
    53  	if batch {
    54  		// do nothing, already deleted with GetValueAndDeleteBatch in getCountsMapValues
    55  		return nil
    56  	}
    57  	for _, key := range keys {
    58  		err := s.mapCounts.DeleteKey(unsafe.Pointer(&key[0]))
    59  		if err != nil {
    60  			return err
    61  		}
    62  	}
    63  	return nil
    64  }
    65  
    66  func (s *Session) clearStacksMap(knownKeys map[uint32]bool) error {
    67  	m := s.mapStacks
    68  	cnt := 0
    69  	errs := 0
    70  	if s.roundNumber%10 == 0 {
    71  		// do a full reset once in a while
    72  		it := m.Iterator()
    73  		var keys [][]byte
    74  		for it.Next() {
    75  			key := it.Key()
    76  			keyCopy := make([]byte, len(key)) // The slice is valid only until the next call to Next.
    77  			copy(keyCopy, key)
    78  			keys = append(keys, keyCopy)
    79  		}
    80  		for _, key := range keys {
    81  			if err := m.DeleteKey(unsafe.Pointer(&key[0])); err != nil {
    82  				errs += 1
    83  			} else {
    84  				cnt += 1
    85  			}
    86  		}
    87  		return nil
    88  	}
    89  	for stackId := range knownKeys {
    90  		k := stackId
    91  		if err := m.DeleteKey(unsafe.Pointer(&k)); err != nil {
    92  			errs += 1
    93  		} else {
    94  			cnt += 1
    95  		}
    96  	}
    97  	return nil
    98  }
    99  
   100  func collectBatchValues(values []byte, count int, valueSize int) [][]byte {
   101  	var value []byte
   102  	var collected [][]byte
   103  	for i := 0; i < count*valueSize; i += valueSize {
   104  		value = values[i : i+valueSize]
   105  		collected = append(collected, value)
   106  	}
   107  	return collected
   108  }