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 }