github.com/grafana/pyroscope@v1.18.0/pkg/model/profile.go (about)

     1  package model
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/cespare/xxhash/v2"
    11  	"github.com/prometheus/prometheus/model/labels"
    12  
    13  	profilev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
    14  	ingestv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1"
    15  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    16  	"github.com/grafana/pyroscope/pkg/util"
    17  )
    18  
    19  // CompareProfile compares the two profiles.
    20  func CompareProfile(a, b *ingestv1.Profile) int64 {
    21  	if a.Timestamp == b.Timestamp {
    22  		return int64(CompareLabelPairs(a.Labels, b.Labels))
    23  	}
    24  	return a.Timestamp - b.Timestamp
    25  }
    26  
    27  // ParseProfileTypeSelector parses the profile selector string.
    28  func ParseProfileTypeSelector(id string) (*typesv1.ProfileType, error) {
    29  	parts := strings.Split(id, ":")
    30  
    31  	if len(parts) != 5 && len(parts) != 6 {
    32  		return nil, fmt.Errorf("profile-type selection must be of the form <name>:<sample-type>:<sample-unit>:<period-type>:<period-unit>(:delta), got(%d): %q", len(parts), id)
    33  	}
    34  	name, sampleType, sampleUnit, periodType, periodUnit := parts[0], parts[1], parts[2], parts[3], parts[4]
    35  	return &typesv1.ProfileType{
    36  		Name:       name,
    37  		ID:         id,
    38  		SampleType: sampleType,
    39  		SampleUnit: sampleUnit,
    40  		PeriodType: periodType,
    41  		PeriodUnit: periodUnit,
    42  	}, nil
    43  }
    44  
    45  // SelectorFromProfileType builds a *label.Matcher from an profile type struct
    46  func SelectorFromProfileType(profileType *typesv1.ProfileType) *labels.Matcher {
    47  	return &labels.Matcher{
    48  		Type:  labels.MatchEqual,
    49  		Name:  LabelNameProfileType,
    50  		Value: profileType.Name + ":" + profileType.SampleType + ":" + profileType.SampleUnit + ":" + profileType.PeriodType + ":" + profileType.PeriodUnit,
    51  	}
    52  }
    53  
    54  type SpanSelector map[uint64]struct{}
    55  
    56  func NewSpanSelector(spans []string) (SpanSelector, error) {
    57  	m := make(map[uint64]struct{}, len(spans))
    58  	b := make([]byte, 8)
    59  	for _, s := range spans {
    60  		if len(s) != 16 {
    61  			return nil, fmt.Errorf("invalid span id length: %q", s)
    62  		}
    63  		if _, err := hex.Decode(b, util.YoloBuf(s)); err != nil {
    64  			return nil, err
    65  		}
    66  		m[binary.LittleEndian.Uint64(b)] = struct{}{}
    67  	}
    68  	return m, nil
    69  }
    70  
    71  func SymbolsPartitionForProfile(ls Labels, partitionLabel string, p *profilev1.Profile) uint64 {
    72  	return xxhash.Sum64String(symbolsPartitionKeyForProfile(ls, partitionLabel, p))
    73  }
    74  
    75  func symbolsPartitionKeyForProfile(ls Labels, partitionLabel string, p *profilev1.Profile) string {
    76  	if partitionLabel == "" {
    77  		// Only use the main binary's file basename as the partition key
    78  		// if the partition label is not specified.
    79  		if len(p.Mapping) > 0 {
    80  			if filenameID := p.Mapping[0].Filename; filenameID > 0 {
    81  				if filename := extractMappingFilename(p.StringTable[filenameID]); filename != "" {
    82  					return filename
    83  				}
    84  			}
    85  		}
    86  		partitionLabel = LabelNameServiceName
    87  	}
    88  	if value := ls.Get(partitionLabel); value != "" {
    89  		return value
    90  	}
    91  	return "unknown"
    92  }
    93  
    94  func extractMappingFilename(filename string) string {
    95  	// See github.com/google/pprof/profile/profile.go
    96  	// It's unlikely that the main binary mapping is one of them.
    97  	if filename == "" ||
    98  		strings.HasPrefix(filename, "[") ||
    99  		strings.HasPrefix(filename, "linux-vdso") ||
   100  		strings.HasPrefix(filename, "/dev/dri/") ||
   101  		strings.HasPrefix(filename, "//anon") {
   102  		return ""
   103  	}
   104  	// Like filepath.ToSlash but doesn't rely on OS.
   105  	n := strings.ReplaceAll(filename, `\`, `/`)
   106  	return strings.TrimSpace(filepath.Base(filepath.Clean(n)))
   107  }