github.com/grafana/pyroscope@v1.18.0/pkg/phlaredb/symdb/stacktrace_tree.go (about) 1 package symdb 2 3 import ( 4 "bufio" 5 "io" 6 "unsafe" 7 8 "github.com/dgryski/go-groupvarint" 9 ) 10 11 const ( 12 defaultStacktraceTreeSize = 0 13 stacktraceTreeNodeSize = int(unsafe.Sizeof(node{})) 14 ) 15 16 type stacktraceTree struct { 17 nodes []node 18 } 19 20 type node struct { 21 p int32 // Parent index. 22 r int32 // Reference the to stack frame data. 23 // Auxiliary members only needed for insertion. 24 fc int32 // First child index. 25 ns int32 // Next sibling index. 26 } 27 28 func newStacktraceTree(size int) *stacktraceTree { 29 if size < 1 { 30 size = 1 31 } 32 t := stacktraceTree{nodes: make([]node, 1, size)} 33 t.nodes[0] = node{ 34 p: sentinel, 35 fc: sentinel, 36 ns: sentinel, 37 } 38 return &t 39 } 40 41 const sentinel = -1 42 43 func (t *stacktraceTree) len() uint32 { return uint32(len(t.nodes)) } 44 45 func (t *stacktraceTree) insert(refs []uint64) uint32 { 46 var ( 47 n = &t.nodes[0] 48 i = n.fc 49 x int32 50 ) 51 52 for j := len(refs) - 1; j >= 0; { 53 r := int32(refs[j]) 54 if i == sentinel { 55 ni := int32(len(t.nodes)) 56 n.fc = ni 57 t.nodes = append(t.nodes, node{ 58 r: r, 59 p: x, 60 fc: sentinel, 61 ns: sentinel, 62 }) 63 x = ni 64 n = &t.nodes[ni] 65 } else { 66 x = i 67 n = &t.nodes[i] 68 } 69 if n.r == r { 70 i = n.fc 71 j-- 72 continue 73 } 74 if n.ns < 0 { 75 n.ns = int32(len(t.nodes)) 76 t.nodes = append(t.nodes, node{ 77 r: r, 78 p: n.p, 79 fc: sentinel, 80 ns: sentinel, 81 }) 82 } 83 i = n.ns 84 } 85 86 return uint32(x) 87 } 88 89 func (t *stacktraceTree) resolve(dst []int32, id uint32) []int32 { 90 dst = dst[:0] 91 if id >= uint32(len(t.nodes)) { 92 return dst 93 } 94 // Only node members are accessed, in order to avoid 95 // race condition with insert: r and p are written once, 96 // when the node is created. 97 for i := int32(id); i > 0; i = t.nodes[i].p { 98 dst = append(dst, t.nodes[i].r) 99 } 100 return dst 101 } 102 103 func (t *stacktraceTree) resolveUint64(dst []uint64, id uint32) []uint64 { 104 dst = dst[:0] 105 if id >= uint32(len(t.nodes)) { 106 return dst 107 } 108 // Only node members are accessed, in order to avoid 109 // race condition with insert: r and p are written once, 110 // when the node is created. 111 for i := int32(id); i > 0; i = t.nodes[i].p { 112 dst = append(dst, uint64(t.nodes[i].r)) 113 } 114 return dst 115 } 116 117 func (t *stacktraceTree) Nodes() []Node { 118 dst := make([]Node, len(t.nodes)) 119 for i := 0; i < len(dst) && i < len(t.nodes); i++ { // BCE 120 dst[i] = Node{Parent: t.nodes[i].p, Location: t.nodes[i].r} 121 } 122 return dst 123 } 124 125 const ( 126 maxGroupSize = 17 // 4 * uint32 + control byte 127 // minGroupSize = 5 // 4 * byte + control byte 128 ) 129 130 func (t *stacktraceTree) WriteTo(dst io.Writer) (int64, error) { 131 e := treeEncoder{ 132 writeSize: 4 << 10, 133 } 134 err := e.marshal(t, dst) 135 return e.written, err 136 } 137 138 type parentPointerTree struct { 139 nodes []pptNode 140 } 141 142 type pptNode struct { 143 p int32 // Parent index. 144 r int32 // Reference the to stack frame data. 145 } 146 147 func newParentPointerTree(size uint32) *parentPointerTree { 148 return &parentPointerTree{ 149 nodes: make([]pptNode, size), 150 } 151 } 152 153 func (t *parentPointerTree) resolve(dst []int32, id uint32) []int32 { 154 dst = dst[:0] 155 if id >= uint32(len(t.nodes)) { 156 return dst 157 } 158 n := t.nodes[id] 159 for n.p >= 0 { 160 dst = append(dst, n.r) 161 n = t.nodes[n.p] 162 } 163 return dst 164 } 165 166 func (t *parentPointerTree) resolveUint64(dst []uint64, id uint32) []uint64 { 167 dst = dst[:0] 168 if id >= uint32(len(t.nodes)) { 169 return dst 170 } 171 n := t.nodes[id] 172 for n.p >= 0 { 173 dst = append(dst, uint64(n.r)) 174 n = t.nodes[n.p] 175 } 176 return dst 177 } 178 179 func (t *parentPointerTree) Nodes() []Node { 180 dst := make([]Node, len(t.nodes)) 181 for i := 0; i < len(dst) && i < len(t.nodes); i++ { // BCE 182 dst[i] = Node{Parent: t.nodes[i].p, Location: t.nodes[i].r} 183 } 184 return dst 185 } 186 187 func (t *parentPointerTree) toStacktraceTree() *stacktraceTree { 188 l := int32(len(t.nodes)) 189 x := stacktraceTree{nodes: make([]node, l)} 190 x.nodes[0] = node{ 191 p: sentinel, 192 fc: sentinel, 193 ns: sentinel, 194 } 195 lc := make([]int32, len(t.nodes)) 196 var s int32 197 for i := int32(1); i < l; i++ { 198 n := t.nodes[i] 199 x.nodes[i] = node{ 200 p: n.p, 201 r: n.r, 202 fc: sentinel, 203 ns: sentinel, 204 } 205 // Swap the last child of the parent with self. 206 // If this is the first child, update the parent. 207 // Otherwise, update the sibling. 208 s, lc[n.p] = lc[n.p], i 209 if s == 0 { 210 x.nodes[n.p].fc = i 211 } else { 212 x.nodes[s].ns = i 213 } 214 } 215 return &x 216 } 217 218 // ReadFrom decodes parent pointer tree from the reader. 219 // The tree must have enough nodes. 220 func (t *parentPointerTree) ReadFrom(r io.Reader) (int64, error) { 221 d := treeDecoder{ 222 bufSize: 4 << 10, 223 peekSize: 4 << 10, 224 groupBuffer: 1 << 10, 225 } 226 err := d.unmarshal(t, r) 227 return d.read, err 228 } 229 230 type treeEncoder struct { 231 writeSize int 232 written int64 233 } 234 235 func (tc *treeEncoder) marshal(t *stacktraceTree, w io.Writer) (err error) { 236 // Writes go through a staging buffer. 237 // Make sure it is allocated on stack. 238 ws := tc.writeSize 239 b := make([]byte, ws) 240 g := make([]uint32, 4) 241 var n, s int 242 // For delta zig-zag. 243 var p, c node 244 var v int32 245 246 for i := 0; i < len(t.nodes); i += 2 { 247 // First node of the pair. 248 c = t.nodes[i] 249 v = c.p - p.p 250 g[0] = uint32((v << 1) ^ (v >> 31)) 251 g[1] = uint32(c.r) 252 p = c 253 if sn := i + 1; sn < len(t.nodes) { 254 // Second node. 255 c = t.nodes[sn] 256 v = c.p - p.p 257 g[2] = uint32((v << 1) ^ (v >> 31)) 258 g[3] = uint32(c.r) 259 p = c 260 } else { 261 // A stub node is added to complete the group. 262 g[2] = 0 263 g[3] = 0 264 } 265 groupvarint.Encode4(b[n:], g) 266 n += groupvarint.BytesUsed[b[n]] 267 if n+maxGroupSize > ws || i >= len(t.nodes)-2 { 268 s, err = w.Write(b[:n]) 269 if err != nil { 270 return err 271 } 272 tc.written += int64(s) 273 n = 0 274 } 275 } 276 return nil 277 } 278 279 type treeDecoder struct { 280 bufSize int 281 peekSize int 282 groupBuffer int // %4 == 0 283 read int64 284 } 285 286 func (d *treeDecoder) unmarshal(t *parentPointerTree, r io.Reader) error { 287 buf, ok := r.(*bufio.Reader) 288 if !ok || buf.Size() < d.peekSize { 289 buf = bufio.NewReaderSize(r, d.bufSize) 290 } 291 292 g := make([]uint32, d.groupBuffer) 293 rb := make([]byte, 0, maxGroupSize) 294 var p, c pptNode // Previous and current nodes. 295 var np int 296 var eof bool 297 298 for !eof { 299 // Load the next peekSize bytes. 300 // Must not exceed Reader's buffer size. 301 b, err := buf.Peek(d.peekSize) 302 if err != nil { 303 if err != io.EOF { 304 return err 305 } 306 eof = true 307 } 308 // len(b) is always >= b.Buffered(), 309 // therefore Discard does not invalidate 310 // the buffer. 311 if _, err = buf.Discard(len(b)); err != nil { 312 return err 313 } 314 d.read += int64(len(b)) 315 316 // Read b into g and decode. 317 for read := 0; read < len(b); { 318 // We need to read remaining_nodes * 2 uints or the whole 319 // group buffer, whichever is smaller. 320 xn := len(t.nodes) - np // remaining nodes 321 // Note that g should always be a multiple of 4. 322 g = g[:min((xn+xn%2)*2, d.groupBuffer)] 323 if len(g)%4 != 0 { 324 return io.ErrUnexpectedEOF 325 } 326 // Check if there is a remainder. If this is the case, 327 // decode the group and advance gp. 328 var gp int 329 if len(rb) > 0 { 330 // It's expected that rb contains a single complete group. 331 m := groupvarint.BytesUsed[rb[0]] - len(rb) 332 if m >= (len(b) + len(rb)) { 333 return io.ErrUnexpectedEOF 334 } 335 rb = append(rb, b[:m]...) 336 i, n, rn := decodeU32Groups(g[:4], rb) 337 if i != 4 || n != len(rb) || rn > 0 { 338 return io.ErrUnexpectedEOF 339 } 340 read += m // Part is read from rb. 341 rb = rb[:0] 342 gp += 4 343 } 344 345 // Re-fill g. 346 gi, n, rn := decodeU32Groups(g[gp:], b[read:]) 347 gp += gi 348 read += n + rn // Mark the remaining bytes as read; we copy them. 349 if rn > 0 { 350 // If there is a remainder, it is copied and decoded on 351 // the next Peek. This should not be possible with eof. 352 rb = append(rb, b[len(b)-rn:]...) 353 } 354 if len(g) == 0 && len(rb) == 0 { 355 break 356 } 357 358 // g is full, or no more data in buf. 359 for i := 0; i < len(g[:gp])-1; i += 2 { 360 if np >= len(t.nodes) { 361 // g may contain an empty node at the end. 362 return nil 363 } 364 v := int32(g[i]) 365 c.p = (v>>1 ^ ((v << 31) >> 31)) + p.p 366 c.r = int32(g[i+1]) 367 t.nodes[np] = c 368 np++ 369 p = c 370 } 371 } 372 } 373 374 return nil 375 } 376 377 // decodeU32Groups decodes len(dst)/4 groups from src and 378 // returns: dst offset, bytes read, bytes remaining in src. 379 func decodeU32Groups(dst []uint32, src []byte) (i, j, rm int) { 380 var n int 381 for i < len(dst) && j < len(src) { 382 n = groupvarint.BytesUsed[src[j]] 383 if rm = len(src[j:]); rm < n { 384 return i, j, rm 385 } 386 groupvarint.Decode4(dst[i:], src[j:]) 387 i += 4 388 j += n 389 } 390 return i, j, 0 391 }