github.com/grafana/pyroscope@v1.18.0/pkg/og/convert/pprof/bench/utils.go (about)

     1  package bench
     2  
     3  import (
     4  	"compress/gzip"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"slices"
     9  	"strings"
    10  
    11  	profilev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
    12  )
    13  
    14  func ReadGzipFile(f string) ([]byte, error) {
    15  	fd, err := os.Open(f)
    16  	if err != nil {
    17  		return nil, err
    18  	}
    19  	defer fd.Close()
    20  	g, err := gzip.NewReader(fd)
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  	return io.ReadAll(g)
    25  }
    26  
    27  func WriteGzipFile(f string, data []byte) error {
    28  	fd, err := os.OpenFile(f, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	defer fd.Close()
    33  	g := gzip.NewWriter(fd)
    34  	_, err = g.Write(data)
    35  	if err != nil {
    36  		return err
    37  	}
    38  	return g.Close()
    39  }
    40  
    41  type StackCollapseOptions struct {
    42  	ValueIdx   int
    43  	Scale      float64
    44  	WithLabels bool
    45  }
    46  
    47  func StackCollapseProto(p *profilev1.Profile, valueIDX int, scale float64) []string {
    48  	return StackCollapseProtoWithOptions(p, StackCollapseOptions{ValueIdx: valueIDX, Scale: scale})
    49  }
    50  
    51  func StackCollapseProtoWithOptions(p *profilev1.Profile, opt StackCollapseOptions) []string {
    52  	var valueIDX int = opt.ValueIdx
    53  	var scale float64 = opt.Scale
    54  	type stack struct {
    55  		funcs string
    56  		value int64
    57  	}
    58  	locMap := make(map[int64]*profilev1.Location)
    59  	funcMap := make(map[int64]*profilev1.Function)
    60  	for _, l := range p.Location {
    61  		locMap[int64(l.Id)] = l
    62  	}
    63  	for _, f := range p.Function {
    64  		funcMap[int64(f.Id)] = f
    65  	}
    66  
    67  	var ret []stack
    68  	for _, s := range p.Sample {
    69  		var funcs []string
    70  		for i := range s.LocationId {
    71  			locID := s.LocationId[len(s.LocationId)-1-i]
    72  			loc := locMap[int64(locID)]
    73  			for _, line := range loc.Line {
    74  				f := funcMap[int64(line.FunctionId)]
    75  				fname := p.StringTable[f.Name]
    76  				funcs = append(funcs, fname)
    77  			}
    78  		}
    79  		v := s.Value[valueIDX]
    80  		if scale != 1 {
    81  			v = int64(float64(v) * scale)
    82  		}
    83  
    84  		sls := ""
    85  		if opt.WithLabels {
    86  			ls := []string{}
    87  			for _, label := range s.Label {
    88  				ls = append(ls, fmt.Sprintf("(%s = %s)", p.StringTable[label.Key], p.StringTable[label.Str]))
    89  			}
    90  			sls = strings.Join(ls, ", ") + " ||| "
    91  		}
    92  		ret = append(ret, stack{
    93  			funcs: sls + strings.Join(funcs, ";"),
    94  			value: v,
    95  		})
    96  	}
    97  	slices.SortFunc(ret, func(i, j stack) int {
    98  		return strings.Compare(i.funcs, j.funcs)
    99  	})
   100  	var unique []stack
   101  	for _, s := range ret {
   102  		if s.value == 0 {
   103  			continue
   104  		}
   105  		if len(unique) == 0 {
   106  			unique = append(unique, s)
   107  			continue
   108  		}
   109  
   110  		if unique[len(unique)-1].funcs == s.funcs {
   111  			unique[len(unique)-1].value += s.value
   112  			continue
   113  		}
   114  		unique = append(unique, s)
   115  	}
   116  
   117  	res := []string{}
   118  	for _, s := range unique {
   119  		res = append(res, fmt.Sprintf("%s %d", s.funcs, s.value))
   120  	}
   121  	return res
   122  }