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  }