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 }