github.com/grafana/pyroscope@v1.18.0/pkg/pprof/fix_go_profile.go (about)

     1  package pprof
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  
     7  	profilev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
     8  )
     9  
    10  // FixGoProfile fixes known issues with profiles collected with
    11  // the standard Go profiler.
    12  //
    13  // Note that the function presumes that p is a Go profile and does
    14  // not verify this. It is expected that the function is called
    15  // very early in the profile processing chain and normalized after,
    16  // regardless of the function outcome.
    17  func FixGoProfile(p *profilev1.Profile) *profilev1.Profile {
    18  	p = DropGoTypeParameters(p)
    19  	// Now that the profile is normalized, we can try to repair
    20  	// truncated stack traces, if any. Note that repaired stacks
    21  	// are not deduplicated, so the caller need to normalize the
    22  	if PotentialTruncatedGoStacktraces(p) {
    23  		RepairGoTruncatedStacktraces(p)
    24  	}
    25  	return p
    26  }
    27  
    28  // DropGoTypeParameters removes of type parameters from Go function names.
    29  //
    30  // In go 1.21 and above, function names include type parameters, however,
    31  // due to a bug, a function name could include any of the type instances
    32  // regardless of the call site. Thus, e.g., x[T1].foo and x[T2].foo can't
    33  // be distinguished in a profile. This leads to incorrect profiles and
    34  // incorrect flame graphs, and hugely increases cardinality of stack traces.
    35  //
    36  // The function renames x[T1].foo and x[T2].foo to x[...].foo and normalizes
    37  // the profile, if type parameters are present in the profile. Otherwise, the
    38  // profile returns unchanged.
    39  //
    40  // See https://github.com/golang/go/issues/64528.
    41  func DropGoTypeParameters(p *profilev1.Profile) *profilev1.Profile {
    42  	var n int
    43  	for i, s := range p.StringTable {
    44  		c := dropGoTypeParameters(s)
    45  		if c != s {
    46  			p.StringTable[i] = c
    47  			n++
    48  		}
    49  	}
    50  	if n == 0 {
    51  		return p
    52  	}
    53  	// Merging with self effectively normalizes the profile:
    54  	// it removed duplicates, establishes order of labels,
    55  	// and allocates monotonic identifiers.
    56  	var m ProfileMerge
    57  	// We safely ignore the error as the only case when it can
    58  	// happen is when merged profiles are of different types.
    59  	_ = m.Merge(p, true)
    60  	return m.Profile()
    61  }
    62  
    63  var goStructTypeParameterRegex = regexp.MustCompile(`\[go\.shape\..*\]`)
    64  
    65  func dropGoTypeParameters(input string) string {
    66  	matchesIndices := goStructTypeParameterRegex.FindAllStringIndex(input, -1)
    67  	if len(matchesIndices) == 0 {
    68  		return input
    69  	}
    70  	var modified strings.Builder
    71  	prevEnd := 0
    72  	for _, indices := range matchesIndices {
    73  		start, end := indices[0], indices[1]
    74  		modified.WriteString(input[prevEnd:start])
    75  		prevEnd = end
    76  	}
    77  	modified.WriteString(input[prevEnd:])
    78  	return modified.String()
    79  }