github.com/grafana/pyroscope@v1.18.0/pkg/phlaredb/symdb/mappings.go (about)

     1  //nolint:unused
     2  package symdb
     3  
     4  import (
     5  	"bytes"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"hash/crc32"
     9  	"io"
    10  	"unsafe"
    11  
    12  	"github.com/parquet-go/parquet-go/encoding/delta"
    13  
    14  	v1 "github.com/grafana/pyroscope/pkg/phlaredb/schemas/v1"
    15  	"github.com/grafana/pyroscope/pkg/slices"
    16  )
    17  
    18  var (
    19  	_ symbolsBlockEncoder[v1.InMemoryMapping] = (*mappingsBlockEncoder)(nil)
    20  	_ symbolsBlockDecoder[v1.InMemoryMapping] = (*mappingsBlockDecoder)(nil)
    21  )
    22  
    23  type mappingsBlockHeader struct {
    24  	MappingsLen  uint32
    25  	FileNameSize uint32
    26  	BuildIDSize  uint32
    27  	FlagsSize    uint32
    28  	// Optional.
    29  	MemoryStartSize uint32
    30  	MemoryLimitSize uint32
    31  	FileOffsetSize  uint32
    32  	CRC             uint32
    33  }
    34  
    35  func (h *mappingsBlockHeader) marshal(b []byte) {
    36  	binary.BigEndian.PutUint32(b[0:4], h.MappingsLen)
    37  	binary.BigEndian.PutUint32(b[4:8], h.FileNameSize)
    38  	binary.BigEndian.PutUint32(b[8:12], h.BuildIDSize)
    39  	binary.BigEndian.PutUint32(b[12:16], h.FlagsSize)
    40  	binary.BigEndian.PutUint32(b[16:20], h.MemoryStartSize)
    41  	binary.BigEndian.PutUint32(b[20:24], h.MemoryLimitSize)
    42  	binary.BigEndian.PutUint32(b[24:28], h.FileOffsetSize)
    43  	// Fields can be added here in the future.
    44  	// CRC must be the last four bytes.
    45  	h.CRC = crc32.Checksum(b[0:28], castagnoli)
    46  	binary.BigEndian.PutUint32(b[28:32], h.CRC)
    47  }
    48  
    49  func (h *mappingsBlockHeader) unmarshal(b []byte) {
    50  	h.MappingsLen = binary.BigEndian.Uint32(b[0:4])
    51  	h.FileNameSize = binary.BigEndian.Uint32(b[4:8])
    52  	h.BuildIDSize = binary.BigEndian.Uint32(b[8:12])
    53  	h.FlagsSize = binary.BigEndian.Uint32(b[12:16])
    54  	h.MemoryStartSize = binary.BigEndian.Uint32(b[16:20])
    55  	h.MemoryLimitSize = binary.BigEndian.Uint32(b[20:24])
    56  	h.FileOffsetSize = binary.BigEndian.Uint32(b[24:28])
    57  	// In future versions, new fields are decoded here;
    58  	// if pos < len(b)-checksumSize, then there are more fields.
    59  	h.CRC = binary.BigEndian.Uint32(b[28:32])
    60  }
    61  
    62  func (h *mappingsBlockHeader) checksum() uint32 { return h.CRC }
    63  
    64  type mappingsBlockEncoder struct {
    65  	header mappingsBlockHeader
    66  
    67  	tmp    []byte
    68  	buf    bytes.Buffer
    69  	ints   []int32
    70  	ints64 []int64
    71  }
    72  
    73  func newMappingsEncoder() *symbolsEncoder[v1.InMemoryMapping] {
    74  	return newSymbolsEncoder[v1.InMemoryMapping](new(mappingsBlockEncoder))
    75  }
    76  
    77  func (e *mappingsBlockEncoder) format() SymbolsBlockFormat { return BlockMappingsV1 }
    78  
    79  func (e *mappingsBlockEncoder) headerSize() uintptr { return unsafe.Sizeof(mappingsBlockHeader{}) }
    80  
    81  func (e *mappingsBlockEncoder) encode(w io.Writer, mappings []v1.InMemoryMapping) error {
    82  	e.initWrite(len(mappings))
    83  	var enc delta.BinaryPackedEncoding
    84  
    85  	for i, m := range mappings {
    86  		e.ints[i] = int32(m.Filename)
    87  	}
    88  	e.tmp, _ = enc.EncodeInt32(e.tmp, e.ints)
    89  	e.header.FileNameSize = uint32(len(e.tmp))
    90  	e.buf.Write(e.tmp)
    91  
    92  	for i, m := range mappings {
    93  		e.ints[i] = int32(m.BuildId)
    94  	}
    95  	e.tmp, _ = enc.EncodeInt32(e.tmp, e.ints)
    96  	e.header.BuildIDSize = uint32(len(e.tmp))
    97  	e.buf.Write(e.tmp)
    98  
    99  	for i, m := range mappings {
   100  		var v int32
   101  		if m.HasFunctions {
   102  			v |= 1 << 3
   103  		}
   104  		if m.HasFilenames {
   105  			v |= 1 << 2
   106  		}
   107  		if m.HasLineNumbers {
   108  			v |= 1 << 1
   109  		}
   110  		if m.HasInlineFrames {
   111  			v |= 1
   112  		}
   113  		e.ints[i] = v
   114  	}
   115  	e.tmp, _ = enc.EncodeInt32(e.tmp, e.ints)
   116  	e.header.FlagsSize = uint32(len(e.tmp))
   117  	e.buf.Write(e.tmp)
   118  
   119  	var memoryStart uint64
   120  	for i, m := range mappings {
   121  		memoryStart |= m.MemoryStart
   122  		e.ints64[i] = int64(m.MemoryStart)
   123  	}
   124  	if memoryStart != 0 {
   125  		e.tmp, _ = enc.EncodeInt64(e.tmp, e.ints64)
   126  		e.header.MemoryStartSize = uint32(len(e.tmp))
   127  		e.buf.Write(e.tmp)
   128  	}
   129  
   130  	var memoryLimit uint64
   131  	for i, m := range mappings {
   132  		memoryLimit |= m.MemoryLimit
   133  		e.ints64[i] = int64(m.MemoryLimit)
   134  	}
   135  	if memoryLimit != 0 {
   136  		e.tmp, _ = enc.EncodeInt64(e.tmp, e.ints64)
   137  		e.header.MemoryLimitSize = uint32(len(e.tmp))
   138  		e.buf.Write(e.tmp)
   139  	}
   140  
   141  	var fileOffset uint64
   142  	for i, m := range mappings {
   143  		fileOffset |= m.FileOffset
   144  		e.ints64[i] = int64(m.FileOffset)
   145  	}
   146  	if fileOffset != 0 {
   147  		e.tmp, _ = enc.EncodeInt64(e.tmp, e.ints64)
   148  		e.header.FileOffsetSize = uint32(len(e.tmp))
   149  		e.buf.Write(e.tmp)
   150  	}
   151  
   152  	e.tmp = slices.GrowLen(e.tmp, int(e.headerSize()))
   153  	e.header.marshal(e.tmp)
   154  	if _, err := w.Write(e.tmp); err != nil {
   155  		return err
   156  	}
   157  	_, err := e.buf.WriteTo(w)
   158  	return err
   159  }
   160  
   161  func (e *mappingsBlockEncoder) initWrite(mappings int) {
   162  	e.buf.Reset()
   163  	// Actual estimate is ~7 bytes per mapping.
   164  	e.buf.Grow(mappings * 8)
   165  	*e = mappingsBlockEncoder{
   166  		header: mappingsBlockHeader{MappingsLen: uint32(mappings)},
   167  
   168  		tmp:    slices.GrowLen(e.tmp, mappings*2),
   169  		ints:   slices.GrowLen(e.ints, mappings),
   170  		ints64: slices.GrowLen(e.ints64, mappings),
   171  		buf:    e.buf,
   172  	}
   173  }
   174  
   175  type mappingsBlockDecoder struct {
   176  	headerSize uint16
   177  	header     mappingsBlockHeader
   178  
   179  	ints   []int32
   180  	ints64 []int64
   181  	buf    []byte
   182  }
   183  
   184  func newMappingsDecoder(h SymbolsBlockHeader) (*symbolsDecoder[v1.InMemoryMapping], error) {
   185  	if h.Format == BlockMappingsV1 {
   186  		headerSize := max(mappingsBlockHeaderMinSize, h.BlockHeaderSize)
   187  		return newSymbolsDecoder[v1.InMemoryMapping](h, &mappingsBlockDecoder{headerSize: headerSize}), nil
   188  	}
   189  	return nil, fmt.Errorf("%w: unknown mappings format: %d", ErrUnknownVersion, h.Format)
   190  }
   191  
   192  // In early versions, block header size is not specified. Must not change.
   193  const mappingsBlockHeaderMinSize = 32
   194  
   195  func (d *mappingsBlockDecoder) decode(r io.Reader, mappings []v1.InMemoryMapping) (err error) {
   196  	d.buf = slices.GrowLen(d.buf, int(d.headerSize))
   197  	if err = readSymbolsBlockHeader(d.buf, r, &d.header); err != nil {
   198  		return err
   199  	}
   200  	if d.header.MappingsLen > uint32(len(mappings)) {
   201  		return fmt.Errorf("mappings buffer is too short")
   202  	}
   203  
   204  	d.ints = slices.GrowLen(d.ints, int(d.header.MappingsLen))
   205  
   206  	d.buf = slices.GrowLen(d.buf, int(d.header.FileNameSize))
   207  	if _, err = io.ReadFull(r, d.buf); err != nil {
   208  		return err
   209  	}
   210  	d.ints, err = decodeBinaryPackedInt32(d.ints, d.buf, int(d.header.MappingsLen))
   211  	if err != nil {
   212  		return err
   213  	}
   214  	for i, v := range d.ints {
   215  		mappings[i].Filename = uint32(v)
   216  	}
   217  
   218  	d.buf = slices.GrowLen(d.buf, int(d.header.BuildIDSize))
   219  	if _, err = io.ReadFull(r, d.buf); err != nil {
   220  		return err
   221  	}
   222  	d.ints, err = decodeBinaryPackedInt32(d.ints, d.buf, int(d.header.MappingsLen))
   223  	if err != nil {
   224  		return err
   225  	}
   226  	for i, v := range d.ints {
   227  		mappings[i].BuildId = uint32(v)
   228  	}
   229  
   230  	d.buf = slices.GrowLen(d.buf, int(d.header.FlagsSize))
   231  	if _, err = io.ReadFull(r, d.buf); err != nil {
   232  		return err
   233  	}
   234  	d.ints, err = decodeBinaryPackedInt32(d.ints, d.buf, int(d.header.MappingsLen))
   235  	if err != nil {
   236  		return err
   237  	}
   238  	for i, v := range d.ints {
   239  		mappings[i].HasFunctions = v&(1<<3) > 0
   240  		mappings[i].HasFilenames = v&(1<<2) > 0
   241  		mappings[i].HasLineNumbers = v&(1<<1) > 0
   242  		mappings[i].HasInlineFrames = v&1 > 0
   243  	}
   244  
   245  	if d.header.MemoryStartSize > 0 {
   246  		d.buf = slices.GrowLen(d.buf, int(d.header.MemoryStartSize))
   247  		if _, err = io.ReadFull(r, d.buf); err != nil {
   248  			return err
   249  		}
   250  		d.ints64, err = decodeBinaryPackedInt64(d.ints64, d.buf, int(d.header.MappingsLen))
   251  		if err != nil {
   252  			return err
   253  		}
   254  		for i, v := range d.ints64 {
   255  			mappings[i].MemoryStart = uint64(v)
   256  		}
   257  	}
   258  	if d.header.MemoryLimitSize > 0 {
   259  		d.buf = slices.GrowLen(d.buf, int(d.header.MemoryLimitSize))
   260  		if _, err = io.ReadFull(r, d.buf); err != nil {
   261  			return err
   262  		}
   263  		d.ints64, err = decodeBinaryPackedInt64(d.ints64, d.buf, int(d.header.MappingsLen))
   264  		if err != nil {
   265  			return err
   266  		}
   267  		for i, v := range d.ints64 {
   268  			mappings[i].MemoryLimit = uint64(v)
   269  		}
   270  	}
   271  	if d.header.FileOffsetSize > 0 {
   272  		d.buf = slices.GrowLen(d.buf, int(d.header.FileOffsetSize))
   273  		if _, err = io.ReadFull(r, d.buf); err != nil {
   274  			return err
   275  		}
   276  		d.ints64, err = decodeBinaryPackedInt64(d.ints64, d.buf, int(d.header.MappingsLen))
   277  		if err != nil {
   278  			return err
   279  		}
   280  		for i, v := range d.ints64 {
   281  			mappings[i].FileOffset = uint64(v)
   282  		}
   283  	}
   284  
   285  	return nil
   286  }