in auditbeat/tracing/decoder.go [173:325]
func NewStructDecoder(desc ProbeFormat, allocFn AllocateFn) (Decoder, error) {
dec := new(structDecoder)
dec.alloc = allocFn
// Validate that allocFn() returns pointers to structs.
sample := allocFn()
tSample := reflect.TypeOf(sample)
if tSample.Kind() != reflect.Ptr {
return nil, errors.New("allocator function doesn't return a pointer")
}
tSample = tSample.Elem()
if tSample.Kind() != reflect.Struct {
return nil, errors.New("allocator function doesn't return a pointer to a struct")
}
var inFieldsByOffset map[int]Field
for i := 0; i < tSample.NumField(); i++ {
outField := tSample.Field(i)
values, found := outField.Tag.Lookup("kprobe")
if !found {
// Untagged field
continue
}
var name string
var allowUndefined bool
var greedy bool
for idx, param := range strings.Split(values, ",") {
switch param {
case "allowundefined":
// it is okay not to find it in the desc.Fields
allowUndefined = true
case "greedy":
greedy = true
default:
if idx != 0 {
return nil, fmt.Errorf("bad parameter '%s' in kprobe tag for field '%s'", param, outField.Name)
}
name = param
}
}
// Special handling for metadata field
if name == "metadata" {
if outField.Type != reflect.TypeOf(Metadata{}) {
return nil, errors.New("bad type for meta field")
}
dec.fields = append(dec.fields, fieldDecoder{
name: name,
typ: FieldTypeMeta,
dst: outField.Offset,
// src&len are unused, this avoids checking len against actual payload
src: 0,
len: 0,
})
continue
}
inField, found := desc.Fields[name]
if !found {
if allowUndefined {
continue
}
return nil, fmt.Errorf("field '%s' not found in kprobe format description", name)
}
if greedy {
// When greedy is used for the first time, build a map of kprobe's
// fields by the offset they appear.
if inFieldsByOffset == nil {
inFieldsByOffset = make(map[int]Field)
for _, v := range desc.Fields {
inFieldsByOffset[v.Offset] = v
}
}
greedySize := uintptr(inField.Size)
nextOffset := inField.Offset + inField.Size
nextFieldID := -1
for {
nextField, found := inFieldsByOffset[nextOffset]
if !found {
break
}
if strings.Index(nextField.Name, "arg") != 0 {
break
}
fieldID, err := strconv.Atoi(nextField.Name[3:])
if err != nil {
break
}
if nextFieldID != -1 && nextFieldID != fieldID {
break
}
greedySize += uintptr(nextField.Size)
nextOffset += nextField.Size
nextFieldID = fieldID + 1
}
if greedySize > maxRawCopySize {
return nil, fmt.Errorf("greedy field '%s' exceeds limit of %d bytes", outField.Name, maxRawCopySize)
}
if curSize := outField.Type.Size(); curSize != greedySize {
return nil, fmt.Errorf("greedy field '%s' size is %d but greedy requires %d", outField.Name, curSize, greedySize)
}
dec.fields = append(dec.fields, fieldDecoder{
name: name,
typ: FieldTypeRaw,
src: uintptr(inField.Offset),
dst: outField.Offset,
len: greedySize,
})
continue
}
switch inField.Type {
case FieldTypeInteger:
if _, found := intFields[outField.Type.Kind()]; !found {
return nil, fmt.Errorf("wrong struct field type for field '%s', fixed size integer required", name)
}
if outField.Type.Size() != uintptr(inField.Size) {
return nil, fmt.Errorf("wrong struct field size for field '%s', got=%d required=%d",
name, outField.Type.Size(), inField.Size)
}
// Paranoid
if inField.Size > maxIntSizeBytes {
return nil, fmt.Errorf("fix me: unexpected integer of size %d in field `%s`",
inField.Size, name)
}
case FieldTypeString:
if outField.Type.Kind() != reflect.String {
return nil, fmt.Errorf("wrong struct field type for field '%s', it should be string", name)
}
default:
// Should not happen
return nil, fmt.Errorf("unexpected field type for field '%s'", name)
}
dec.fields = append(dec.fields, fieldDecoder{
typ: inField.Type,
src: uintptr(inField.Offset),
dst: outField.Offset,
len: uintptr(inField.Size),
name: name,
})
}
sort.Slice(dec.fields, func(i, j int) bool {
return dec.fields[i].src < dec.fields[j].src
})
return dec, nil
}