mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
b79dec8138
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
253 lines
6.6 KiB
Go
253 lines
6.6 KiB
Go
package ebpf
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"runtime"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"github.com/cilium/ebpf/internal"
|
|
)
|
|
|
|
// marshalPtr converts an arbitrary value into a pointer suitable
|
|
// to be passed to the kernel.
|
|
//
|
|
// As an optimization, it returns the original value if it is an
|
|
// unsafe.Pointer.
|
|
func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
|
|
if ptr, ok := data.(unsafe.Pointer); ok {
|
|
return internal.NewPointer(ptr), nil
|
|
}
|
|
|
|
buf, err := marshalBytes(data, length)
|
|
if err != nil {
|
|
return internal.Pointer{}, err
|
|
}
|
|
|
|
return internal.NewSlicePointer(buf), nil
|
|
}
|
|
|
|
// marshalBytes converts an arbitrary value into a byte buffer.
|
|
//
|
|
// Prefer using Map.marshalKey and Map.marshalValue if possible, since
|
|
// those have special cases that allow more types to be encoded.
|
|
//
|
|
// Returns an error if the given value isn't representable in exactly
|
|
// length bytes.
|
|
func marshalBytes(data interface{}, length int) (buf []byte, err error) {
|
|
if data == nil {
|
|
return nil, errors.New("can't marshal a nil value")
|
|
}
|
|
|
|
switch value := data.(type) {
|
|
case encoding.BinaryMarshaler:
|
|
buf, err = value.MarshalBinary()
|
|
case string:
|
|
buf = []byte(value)
|
|
case []byte:
|
|
buf = value
|
|
case unsafe.Pointer:
|
|
err = errors.New("can't marshal from unsafe.Pointer")
|
|
case Map, *Map, Program, *Program:
|
|
err = fmt.Errorf("can't marshal %T", value)
|
|
default:
|
|
var wr bytes.Buffer
|
|
err = binary.Write(&wr, internal.NativeEndian, value)
|
|
if err != nil {
|
|
err = fmt.Errorf("encoding %T: %v", value, err)
|
|
}
|
|
buf = wr.Bytes()
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(buf) != length {
|
|
return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length)
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) {
|
|
if ptr, ok := dst.(unsafe.Pointer); ok {
|
|
return internal.NewPointer(ptr), nil
|
|
}
|
|
|
|
buf := make([]byte, length)
|
|
return internal.NewSlicePointer(buf), buf
|
|
}
|
|
|
|
var bytesReaderPool = sync.Pool{
|
|
New: func() interface{} {
|
|
return new(bytes.Reader)
|
|
},
|
|
}
|
|
|
|
// unmarshalBytes converts a byte buffer into an arbitrary value.
|
|
//
|
|
// Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since
|
|
// those have special cases that allow more types to be encoded.
|
|
//
|
|
// The common int32 and int64 types are directly handled to avoid
|
|
// unnecessary heap allocations as happening in the default case.
|
|
func unmarshalBytes(data interface{}, buf []byte) error {
|
|
switch value := data.(type) {
|
|
case unsafe.Pointer:
|
|
var dst []byte
|
|
// Use unsafe.Slice when we drop support for pre1.17 (https://github.com/golang/go/issues/19367)
|
|
// We could opt for removing unsafe.Pointer support in the lib as well
|
|
sh := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
|
|
sh.Data = uintptr(value)
|
|
sh.Len = len(buf)
|
|
sh.Cap = len(buf)
|
|
|
|
copy(dst, buf)
|
|
runtime.KeepAlive(value)
|
|
return nil
|
|
case Map, *Map, Program, *Program:
|
|
return fmt.Errorf("can't unmarshal into %T", value)
|
|
case encoding.BinaryUnmarshaler:
|
|
return value.UnmarshalBinary(buf)
|
|
case *string:
|
|
*value = string(buf)
|
|
return nil
|
|
case *[]byte:
|
|
*value = buf
|
|
return nil
|
|
case *int32:
|
|
if len(buf) < 4 {
|
|
return errors.New("int32 requires 4 bytes")
|
|
}
|
|
*value = int32(internal.NativeEndian.Uint32(buf))
|
|
return nil
|
|
case *uint32:
|
|
if len(buf) < 4 {
|
|
return errors.New("uint32 requires 4 bytes")
|
|
}
|
|
*value = internal.NativeEndian.Uint32(buf)
|
|
return nil
|
|
case *int64:
|
|
if len(buf) < 8 {
|
|
return errors.New("int64 requires 8 bytes")
|
|
}
|
|
*value = int64(internal.NativeEndian.Uint64(buf))
|
|
return nil
|
|
case *uint64:
|
|
if len(buf) < 8 {
|
|
return errors.New("uint64 requires 8 bytes")
|
|
}
|
|
*value = internal.NativeEndian.Uint64(buf)
|
|
return nil
|
|
case string:
|
|
return errors.New("require pointer to string")
|
|
case []byte:
|
|
return errors.New("require pointer to []byte")
|
|
default:
|
|
rd := bytesReaderPool.Get().(*bytes.Reader)
|
|
rd.Reset(buf)
|
|
defer bytesReaderPool.Put(rd)
|
|
if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
|
|
return fmt.Errorf("decoding %T: %v", value, err)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// marshalPerCPUValue encodes a slice containing one value per
|
|
// possible CPU into a buffer of bytes.
|
|
//
|
|
// Values are initialized to zero if the slice has less elements than CPUs.
|
|
//
|
|
// slice must have a type like []elementType.
|
|
func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) {
|
|
sliceType := reflect.TypeOf(slice)
|
|
if sliceType.Kind() != reflect.Slice {
|
|
return internal.Pointer{}, errors.New("per-CPU value requires slice")
|
|
}
|
|
|
|
possibleCPUs, err := internal.PossibleCPUs()
|
|
if err != nil {
|
|
return internal.Pointer{}, err
|
|
}
|
|
|
|
sliceValue := reflect.ValueOf(slice)
|
|
sliceLen := sliceValue.Len()
|
|
if sliceLen > possibleCPUs {
|
|
return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
|
|
}
|
|
|
|
alignedElemLength := internal.Align(elemLength, 8)
|
|
buf := make([]byte, alignedElemLength*possibleCPUs)
|
|
|
|
for i := 0; i < sliceLen; i++ {
|
|
elem := sliceValue.Index(i).Interface()
|
|
elemBytes, err := marshalBytes(elem, elemLength)
|
|
if err != nil {
|
|
return internal.Pointer{}, err
|
|
}
|
|
|
|
offset := i * alignedElemLength
|
|
copy(buf[offset:offset+elemLength], elemBytes)
|
|
}
|
|
|
|
return internal.NewSlicePointer(buf), nil
|
|
}
|
|
|
|
// unmarshalPerCPUValue decodes a buffer into a slice containing one value per
|
|
// possible CPU.
|
|
//
|
|
// valueOut must have a type like *[]elementType
|
|
func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
|
|
slicePtrType := reflect.TypeOf(slicePtr)
|
|
if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
|
|
return fmt.Errorf("per-cpu value requires pointer to slice")
|
|
}
|
|
|
|
possibleCPUs, err := internal.PossibleCPUs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sliceType := slicePtrType.Elem()
|
|
slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs)
|
|
|
|
sliceElemType := sliceType.Elem()
|
|
sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
|
|
if sliceElemIsPointer {
|
|
sliceElemType = sliceElemType.Elem()
|
|
}
|
|
|
|
step := len(buf) / possibleCPUs
|
|
if step < elemLength {
|
|
return fmt.Errorf("per-cpu element length is larger than available data")
|
|
}
|
|
for i := 0; i < possibleCPUs; i++ {
|
|
var elem interface{}
|
|
if sliceElemIsPointer {
|
|
newElem := reflect.New(sliceElemType)
|
|
slice.Index(i).Set(newElem)
|
|
elem = newElem.Interface()
|
|
} else {
|
|
elem = slice.Index(i).Addr().Interface()
|
|
}
|
|
|
|
// Make a copy, since unmarshal can hold on to itemBytes
|
|
elemBytes := make([]byte, elemLength)
|
|
copy(elemBytes, buf[:elemLength])
|
|
|
|
err := unmarshalBytes(elem, elemBytes)
|
|
if err != nil {
|
|
return fmt.Errorf("cpu %d: %w", i, err)
|
|
}
|
|
|
|
buf = buf[step:]
|
|
}
|
|
|
|
reflect.ValueOf(slicePtr).Elem().Set(slice)
|
|
return nil
|
|
}
|