github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/profile/pyroscope/pprof/pyroscope.go (about)

     1  // Copyright 2023 iLogtail Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pprof
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/pyroscope-io/pyroscope/pkg/storage/metadata"
    21  	"github.com/pyroscope-io/pyroscope/pkg/storage/segment"
    22  	"github.com/pyroscope-io/pyroscope/pkg/storage/tree"
    23  
    24  	"github.com/alibaba/ilogtail/pkg/helper/profile"
    25  )
    26  
    27  type StackFrameFormatter interface {
    28  	format(x *tree.Profile, fn *tree.Function, line *tree.Line) []byte
    29  }
    30  
    31  type Formatter struct {
    32  }
    33  
    34  func (Formatter) format(x *tree.Profile, fn *tree.Function, _ *tree.Line) []byte {
    35  	return []byte(fmt.Sprintf("%s %s",
    36  		x.StringTable[fn.Name],
    37  		x.StringTable[fn.Filename],
    38  	))
    39  }
    40  
    41  func filterKnownSamples(sampleTypes map[string]*tree.SampleTypeConfig) func(string) bool {
    42  	return func(s string) bool {
    43  		_, ok := sampleTypes[s]
    44  		return ok
    45  	}
    46  }
    47  
    48  type Parser struct {
    49  	stackFrameFormatter StackFrameFormatter
    50  	sampleTypesFilter   func(string) bool
    51  	sampleTypes         map[string]*tree.SampleTypeConfig
    52  	cache               tree.LabelsCache
    53  }
    54  
    55  func (p *Parser) getDisplayName(defaultName string) string {
    56  	config, ok := p.sampleTypes[defaultName]
    57  	if !ok || config.DisplayName == "" {
    58  		return defaultName
    59  	}
    60  	return config.DisplayName
    61  }
    62  
    63  func (p *Parser) getAggregationType(name string, defaultval string) string {
    64  	config, ok := p.sampleTypes[name]
    65  	if !ok {
    66  		return defaultval
    67  	}
    68  	switch config.Aggregation {
    69  	case metadata.AverageAggregationType:
    70  		return string(profile.AvgAggType)
    71  	case metadata.SumAggregationType:
    72  		return string(profile.SumAggType)
    73  	default:
    74  		return defaultval
    75  	}
    76  }
    77  
    78  func (p *Parser) iterate(x *tree.Profile, fn func(vt *tree.ValueType, l tree.Labels, t *tree.Tree) (keep bool, err error)) error {
    79  	c := make(tree.LabelsCache)
    80  	p.readTrees(x, c, tree.NewFinder(x))
    81  	for sampleType, entries := range c {
    82  		if t, ok := x.ResolveSampleType(sampleType); ok {
    83  			for h, e := range entries {
    84  				keep, err := fn(t, e.Labels, e.Tree)
    85  				if err != nil {
    86  					return err
    87  				}
    88  				if !keep {
    89  					c.Remove(sampleType, h)
    90  				}
    91  			}
    92  		}
    93  	}
    94  	p.cache = c
    95  	return nil
    96  }
    97  
    98  func (p *Parser) load(sampleType int64, labels tree.Labels) (*tree.Tree, bool) {
    99  	e, ok := p.cache.Get(sampleType, labels.Hash())
   100  	if !ok {
   101  		return nil, false
   102  	}
   103  	return e.Tree, true
   104  }
   105  
   106  // readTrees generates trees from the profile populating c.
   107  func (p *Parser) readTrees(x *tree.Profile, c tree.LabelsCache, f tree.Finder) {
   108  	// SampleType value indexes.
   109  	indexes := make([]int, 0, len(x.SampleType))
   110  	// Corresponding type IDs used as the main cache keys.
   111  	types := make([]int64, 0, len(x.SampleType))
   112  	for i, s := range x.SampleType {
   113  		if p.sampleTypesFilter != nil && p.sampleTypesFilter(x.StringTable[s.Type]) {
   114  			indexes = append(indexes, i)
   115  			types = append(types, s.Type)
   116  		}
   117  	}
   118  	if len(indexes) == 0 {
   119  		return
   120  	}
   121  	stack := make([][]byte, 0, 16)
   122  	for _, s := range x.Sample {
   123  		for i := len(s.LocationId) - 1; i >= 0; i-- {
   124  			// Resolve stack.
   125  			loc, ok := f.FindLocation(s.LocationId[i])
   126  			if !ok {
   127  				continue
   128  			}
   129  			// Multiple line indicates this location has inlined functions,
   130  			// where the last entry represents the caller into which the
   131  			// preceding entries were inlined.
   132  			//
   133  			// E.g., if memcpy() is inlined into printf:
   134  			//    line[0].function_name == "memcpy"
   135  			//    line[1].function_name == "printf"
   136  			//
   137  			// Therefore iteration goes in reverse order.
   138  			for j := len(loc.Line) - 1; j >= 0; j-- {
   139  				fn, ok := f.FindFunction(loc.Line[j].FunctionId)
   140  				if !ok || x.StringTable[fn.Name] == "" {
   141  					continue
   142  				}
   143  				sf := p.stackFrameFormatter.format(x, fn, loc.Line[j])
   144  				stack = append(stack, sf)
   145  			}
   146  		}
   147  		// Insert tree nodes.
   148  		for i, vi := range indexes {
   149  			v := uint64(s.Value[vi])
   150  			if v == 0 {
   151  				continue
   152  			}
   153  			// If the sample has ProfileID label, it belongs to an exemplar.
   154  			if j := labelIndex(x, s.Label, segment.ProfileIDLabelName); j >= 0 {
   155  				// Regardless of whether we should skip exemplars or not, the value
   156  				// should be appended to the exemplar baseline profile (w/o ProfileID label).
   157  				c.GetOrCreateTree(types[i], tree.CutLabel(s.Label, j)).InsertStack(stack, v)
   158  			}
   159  			c.GetOrCreateTree(types[i], s.Label).InsertStack(stack, v)
   160  		}
   161  		stack = stack[:0]
   162  	}
   163  }
   164  
   165  func labelIndex(p *tree.Profile, labels tree.Labels, key string) int {
   166  	for i, label := range labels {
   167  		if n, ok := p.ResolveLabelName(label); ok && n == key {
   168  			return i
   169  		}
   170  	}
   171  	return -1
   172  }