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  }