func NewStructDecoder()

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
}