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

     1  package symdb
     2  
     3  import (
     4  	"strings"
     5  	"unsafe"
     6  
     7  	googlev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
     8  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
     9  	schemav1 "github.com/grafana/pyroscope/pkg/phlaredb/schemas/v1"
    10  )
    11  
    12  type pprofGoPGO struct {
    13  	profile googlev1.Profile
    14  	symbols *Symbols
    15  	samples *schemav1.Samples
    16  	pgo     *typesv1.GoPGO
    17  	stacks  map[string]*googlev1.Sample
    18  	lut     []uint32
    19  	cur     int
    20  }
    21  
    22  func (r *pprofGoPGO) init(symbols *Symbols, samples schemav1.Samples) {
    23  	r.symbols = symbols
    24  	r.samples = &samples
    25  	// It's expected that after trimmed, most of
    26  	// the stack traces will be deduplicated.
    27  	r.stacks = make(map[string]*googlev1.Sample, samples.Len()/50)
    28  }
    29  
    30  func (r *pprofGoPGO) InsertStacktrace(_ uint32, locations []int32) {
    31  	if len(locations) == 0 {
    32  		r.cur++
    33  		return
    34  	}
    35  	if n := int(r.pgo.KeepLocations); n > 0 && len(locations) > n {
    36  		locations = locations[:n]
    37  	}
    38  	// Trimming implies that many samples will have the same
    39  	// stack trace (the expected value for keepLocs is 5).
    40  	// Therefore, the map is read-intensive: we speculatively
    41  	// reuse capacity of the locations slice as the key, and
    42  	// only copy it, if the insertion into the map is required.
    43  	k := int32sliceString(locations)
    44  	sample, ok := r.stacks[k]
    45  	if !ok {
    46  		sample = &googlev1.Sample{
    47  			LocationId: make([]uint64, len(locations)),
    48  			Value:      []int64{0},
    49  		}
    50  		for i, v := range locations {
    51  			sample.LocationId[i] = uint64(v)
    52  		}
    53  		// Do not retain the input slice.
    54  		r.stacks[strings.Clone(k)] = sample
    55  	}
    56  	sample.Value[0] += int64(r.samples.Values[r.cur])
    57  	r.cur++
    58  }
    59  
    60  func int32sliceString(u []int32) string {
    61  	return unsafe.String((*byte)(unsafe.Pointer(&u[0])), len(u)*4)
    62  }
    63  
    64  func (r *pprofGoPGO) buildPprof() *googlev1.Profile {
    65  	createSampleTypeStub(&r.profile)
    66  	r.appendSamples()
    67  	if r.symbols != nil {
    68  		copyLocations(&r.profile, r.symbols, r.lut)
    69  		copyFunctions(&r.profile, r.symbols, r.lut)
    70  		copyMappings(&r.profile, r.symbols, r.lut)
    71  		copyStrings(&r.profile, r.symbols, r.lut)
    72  	}
    73  	if r.pgo.AggregateCallees {
    74  		// Actual aggregation occurs at pprof.Merge call after
    75  		// the profile is built. All unreferenced objects are
    76  		// to be removed from the profile, and samples with
    77  		// matching stack traces are to be merged.
    78  		r.clearCalleeLineNumber()
    79  	}
    80  	return &r.profile
    81  }
    82  
    83  func (r *pprofGoPGO) appendSamples() {
    84  	r.profile.Sample = make([]*googlev1.Sample, len(r.stacks))
    85  	var i int
    86  	for _, s := range r.stacks {
    87  		r.profile.Sample[i] = s
    88  		i++
    89  	}
    90  }
    91  
    92  func (r *pprofGoPGO) clearCalleeLineNumber() {
    93  	for _, s := range r.profile.Sample {
    94  		r.profile.Location[s.LocationId[0]-1].Line[0].Line = 0
    95  	}
    96  }