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 }