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

     1  package symdb
     2  
     3  import (
     4  	schemav1 "github.com/grafana/pyroscope/pkg/phlaredb/schemas/v1"
     5  )
     6  
     7  // StacktraceIDRange represents a range of stack trace
     8  // identifiers, sharing the same parent pointer tree.
     9  type StacktraceIDRange struct {
    10  	// Stack trace identifiers that belong to the range.
    11  	// Identifiers are relative to the range Offset().
    12  	IDs   []uint32
    13  	chunk uint32 // Chunk index.
    14  	m     uint32 // Max nodes per chunk.
    15  	// Parent pointer tree, the stack traces refer to.
    16  	// A stack trace identifier is the index of the node
    17  	// in the tree. Optional.
    18  	ParentPointerTree
    19  	// Samples matching the stack trace range. Optional.
    20  	schemav1.Samples
    21  	// TODO(kolesnikovae): use SampleAppender instead of Samples.
    22  	//  This will allow to avoid copying the samples.
    23  }
    24  
    25  // SetNodeValues sets the values of the provided Samples to the matching
    26  // parent pointer tree nodes.
    27  func (r *StacktraceIDRange) SetNodeValues(dst []Node) {
    28  	for i := 0; i < len(r.IDs); i++ {
    29  		x := r.StacktraceIDs[i]
    30  		v := int64(r.Values[i])
    31  		if x > 0 && v > 0 && dst[x].Location&truncationMark == 0 {
    32  			dst[x].Value = v
    33  		}
    34  	}
    35  }
    36  
    37  // Offset returns the lowest identifier of the range.
    38  // Identifiers are relative to the range offset.
    39  func (r *StacktraceIDRange) Offset() uint32 { return r.m * r.chunk }
    40  
    41  // SplitStacktraces splits the range of stack trace IDs by limit n into
    42  // sub-ranges matching to the corresponding chunks and shifts the values
    43  // accordingly. Note that the input s is modified in place.
    44  //
    45  // stack trace ID 0 is reserved and is not expected at the input.
    46  // stack trace ID % max_nodes == 0 is not expected as well.
    47  func SplitStacktraces(s []uint32, n uint32) []*StacktraceIDRange {
    48  	if s[len(s)-1] < n || n == 0 {
    49  		// Fast path, just one chunk: the highest stack trace ID
    50  		// is less than the chunk size, or the size is not limited.
    51  		// It's expected that in most cases we'll end up here.
    52  		return []*StacktraceIDRange{{m: n, IDs: s}}
    53  	}
    54  
    55  	var (
    56  		loi int
    57  		lov = (s[0] / n) * n // Lowest possible value for the current chunk.
    58  		hiv = lov + n        // Highest possible value for the current chunk.
    59  		p   uint32           // Previous value (to derive chunk index).
    60  		// 16 chunks should be more than enough in most cases.
    61  		cs = make([]*StacktraceIDRange, 0, 16)
    62  	)
    63  
    64  	for i, v := range s {
    65  		if v < hiv {
    66  			// The stack belongs to the current chunk.
    67  			s[i] -= lov
    68  			p = v
    69  			continue
    70  		}
    71  		lov = (v / n) * n
    72  		hiv = lov + n
    73  		s[i] -= lov
    74  		cs = append(cs, &StacktraceIDRange{
    75  			chunk: p / n,
    76  			IDs:   s[loi:i],
    77  			m:     n,
    78  		})
    79  		loi = i
    80  		p = v
    81  	}
    82  
    83  	if t := s[loi:]; len(t) > 0 {
    84  		cs = append(cs, &StacktraceIDRange{
    85  			chunk: p / n,
    86  			IDs:   t,
    87  			m:     n,
    88  		})
    89  	}
    90  
    91  	return cs
    92  }