github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/storage/tree/profile_extra.go (about) 1 package tree 2 3 // These functions are kept separately as profile.pb.go is a generated file 4 5 import ( 6 "encoding/binary" 7 "sort" 8 9 "github.com/cespare/xxhash/v2" 10 "github.com/pyroscope-io/pyroscope/pkg/agent/spy" 11 "github.com/valyala/bytebufferpool" 12 ) 13 14 type cacheKey []int64 15 16 type cacheEntry struct { 17 key cacheKey 18 val *spy.Labels 19 } 20 type cache struct { 21 data []*cacheEntry 22 } 23 24 func newCache() *cache { 25 return &cache{ 26 data: []*cacheEntry{}, 27 } 28 } 29 30 func getCacheKey(l []*Label) cacheKey { 31 r := []int64{} 32 for _, x := range l { 33 if x.Str != 0 { 34 r = append(r, x.Key, x.Str) 35 } 36 } 37 return r 38 } 39 40 func eq(a, b []int64) bool { 41 if len(a) != len(b) { 42 return false 43 } 44 for i, v := range a { 45 if v != b[i] { 46 return false 47 } 48 } 49 return true 50 } 51 52 func (c *cache) pprofLabelsToSpyLabels(x *Profile, pprofLabels []*Label) *spy.Labels { 53 k := getCacheKey(pprofLabels) 54 for _, e := range c.data { 55 if eq(e.key, k) { 56 return e.val 57 } 58 } 59 60 l := spy.NewLabels() 61 for _, pl := range pprofLabels { 62 if pl.Str != 0 { 63 l.Set(x.StringTable[pl.Key], x.StringTable[pl.Str]) 64 } 65 } 66 newVal := &cacheEntry{ 67 key: k, 68 val: l, 69 } 70 c.data = append(c.data, newVal) 71 return l 72 } 73 74 func (x *Profile) Get(sampleType string, cb func(labels *spy.Labels, name []byte, val int) error) error { 75 valueIndex := 0 76 if sampleType != "" { 77 for i, v := range x.SampleType { 78 if x.StringTable[v.Type] == sampleType { 79 valueIndex = i 80 break 81 } 82 } 83 } 84 85 labelsCache := newCache() 86 87 b := bytebufferpool.Get() 88 defer bytebufferpool.Put(b) 89 90 for _, s := range x.Sample { 91 for i := len(s.LocationId) - 1; i >= 0; i-- { 92 name, ok := FindFunctionName(x, s.LocationId[i]) 93 if !ok { 94 continue 95 } 96 if b.Len() > 0 { 97 _ = b.WriteByte(';') 98 } 99 _, _ = b.WriteString(name) 100 } 101 102 labels := labelsCache.pprofLabelsToSpyLabels(x, s.Label) 103 if err := cb(labels, b.Bytes(), int(s.Value[valueIndex])); err != nil { 104 return err 105 } 106 107 b.Reset() 108 } 109 110 return nil 111 } 112 113 func (x *Profile) SampleTypes() []string { 114 r := []string{} 115 for _, v := range x.SampleType { 116 r = append(r, x.StringTable[v.Type]) 117 } 118 return r 119 } 120 121 func FindFunctionName(x *Profile, locID uint64) (string, bool) { 122 if loc, ok := FindLocation(x, locID); ok { 123 if len(loc.Line) <= 0 { 124 return "", false 125 } 126 127 if fn, ok := FindFunction(x, loc.Line[0].FunctionId); ok { 128 return x.StringTable[fn.Name], true 129 } 130 } 131 return "", false 132 } 133 134 func FindLocation(x *Profile, lid uint64) (*Location, bool) { 135 idx := sort.Search(len(x.Location), func(i int) bool { 136 return x.Location[i].Id >= lid 137 }) 138 if idx < len(x.Location) { 139 if l := x.Location[idx]; l.Id == lid { 140 return l, true 141 } 142 } 143 return nil, false 144 } 145 146 func FindFunction(x *Profile, fid uint64) (*Function, bool) { 147 idx := sort.Search(len(x.Function), func(i int) bool { 148 return x.Function[i].Id >= fid 149 }) 150 if idx < len(x.Function) { 151 if f := x.Function[idx]; f.Id == fid { 152 return f, true 153 } 154 } 155 return nil, false 156 } 157 158 func (x *Profile) ResolveLabels(l Labels) map[string]string { 159 m := make(map[string]string, len(l)) 160 for _, label := range l { 161 if label.Str != 0 { 162 m[x.StringTable[label.Key]] = x.StringTable[label.Str] 163 } 164 } 165 return m 166 } 167 168 func (x *Profile) ResolveLabelName(l *Label) (string, bool) { 169 if l.Str > 0 && l.Key < int64(len(x.StringTable)) { 170 return x.StringTable[l.Key], true 171 } 172 return "", false 173 } 174 175 func (x *Profile) ResolveSampleType(v int64) (*ValueType, bool) { 176 for _, vt := range x.SampleType { 177 if vt.Type == v { 178 return vt, true 179 } 180 } 181 return nil, false 182 } 183 184 type Labels []*Label 185 186 func (l Labels) Len() int { return len(l) } 187 func (l Labels) Less(i, j int) bool { return l[i].Key < l[j].Key } 188 func (l Labels) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 189 190 func (l Labels) Hash() uint64 { 191 h := xxhash.New() 192 t := make([]byte, 16) 193 sort.Sort(l) 194 for _, x := range l { 195 if x.Str == 0 { 196 continue 197 } 198 binary.LittleEndian.PutUint64(t[0:8], uint64(x.Key)) 199 binary.LittleEndian.PutUint64(t[8:16], uint64(x.Str)) 200 _, _ = h.Write(t) 201 } 202 return h.Sum64() 203 }