github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/convert/pprof/streaming/parser_streaming_writebatch.go (about)

     1  package streaming
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"github.com/cespare/xxhash/v2"
     7  	"github.com/pyroscope-io/pyroscope/pkg/stackbuilder"
     8  	"github.com/pyroscope-io/pyroscope/pkg/util/arenahelper"
     9  	"reflect"
    10  	"runtime/debug"
    11  	"time"
    12  	"unsafe"
    13  )
    14  
    15  type ParseWriteBatchInput struct {
    16  	Context            context.Context
    17  	StartTime, EndTime time.Time
    18  	Profile, Previous  []byte
    19  	WriteBatchFactory  stackbuilder.WriteBatchFactory
    20  }
    21  
    22  func (p *VTStreamingParser) ParseWithWriteBatch(input ParseWriteBatchInput) (err error) {
    23  	defer func() {
    24  		if recover() != nil {
    25  			err = fmt.Errorf(fmt.Sprintf("parse panic %s", debug.Stack()))
    26  		}
    27  	}()
    28  	p.startTime = input.StartTime
    29  	p.endTime = input.EndTime
    30  	p.ctx = input.Context
    31  	p.wbf = input.WriteBatchFactory
    32  	p.cumulative = input.Previous != nil
    33  	p.cumulativeOnly = input.Previous != nil
    34  	p.wbCache.cumulative = p.cumulative
    35  	if input.Previous != nil {
    36  		err = p.parseWB(input.Previous, true)
    37  	}
    38  	if err == nil {
    39  		p.cumulativeOnly = false
    40  		err = p.parseWB(input.Profile, false)
    41  	}
    42  	p.ctx = nil
    43  	p.wbf = nil
    44  	return nil
    45  }
    46  
    47  func (p *VTStreamingParser) parseWB(profile []byte, prev bool) (err error) {
    48  	return decompress(profile, func(profile []byte) error {
    49  		p.profile = profile
    50  		p.prev = prev
    51  		err := p.parseDecompressedWB()
    52  		p.profile = nil
    53  		return err
    54  	})
    55  }
    56  
    57  func (p *VTStreamingParser) parseDecompressedWB() (err error) {
    58  	if err = p.countStructs(); err != nil {
    59  		return err
    60  	}
    61  	if err = p.parseFunctionsAndLocations(); err != nil {
    62  		return err
    63  	}
    64  	if !p.haveKnownSampleTypes() {
    65  		return nil
    66  	}
    67  	err = p.UnmarshalVTProfile(p.profile, opFlagParseSamplesWriteBatch)
    68  	if !p.prev {
    69  		p.wbCache.flush()
    70  	}
    71  	return err
    72  }
    73  
    74  func (p *VTStreamingParser) parseSampleWB(buffer []byte) error {
    75  	p.tmpSample.reset(p.arena)
    76  	err := p.tmpSample.UnmarshalSampleVT(buffer, p.arena)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	for i := len(p.tmpSample.tmpStackLoc) - 1; i >= 0; i-- {
    82  		err = p.addStackLocation(p.tmpSample.tmpStackLoc[i])
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  
    88  	cont := p.mergeCumulativeWB()
    89  	if !cont {
    90  		return nil
    91  	}
    92  	p.appendWB()
    93  	return nil
    94  }
    95  
    96  func (p *VTStreamingParser) appendWB() {
    97  	for _, vi := range p.indexes {
    98  		v := uint64(p.tmpSample.tmpValues[vi])
    99  		if v == 0 {
   100  			continue
   101  		}
   102  		wb := p.wbCache.getWriteBatch(p, vi)
   103  		if wb == nil {
   104  			continue
   105  		}
   106  
   107  		sb := wb.wb.StackBuilder()
   108  		sb.Reset()
   109  		for _, frame := range p.tmpSample.tmpStack {
   110  			sb.Push(frame)
   111  		}
   112  		stackID := sb.Build()
   113  
   114  		wb.getAppender(p, p.tmpSample.tmpLabels).Append(stackID, v)
   115  
   116  		if !p.cumulative {
   117  			if j := findLabelIndex(p.tmpSample.tmpLabels, p.profileIDLabelIndex); j >= 0 {
   118  				copy(p.tmpSample.tmpLabels[j:], p.tmpSample.tmpLabels[j+1:])
   119  				p.tmpSample.tmpLabels = p.tmpSample.tmpLabels[:len(p.tmpSample.tmpLabels)-1]
   120  				wb.getAppender(p, p.tmpSample.tmpLabels).Append(stackID, v)
   121  			}
   122  		}
   123  	}
   124  }
   125  
   126  func (p *VTStreamingParser) mergeCumulativeWB() (cont bool) {
   127  	if !p.cumulative {
   128  		return true
   129  	}
   130  
   131  	h := p.tmpSample.hashStack(p.arena)
   132  	for _, vi := range p.indexes {
   133  		v := p.tmpSample.tmpValues[vi]
   134  		if v == 0 {
   135  			continue
   136  		}
   137  		wb := p.wbCache.getWriteBatch(p, vi)
   138  		if wb == nil {
   139  			continue
   140  		}
   141  		if p.prev {
   142  			wb.prev[h] += v
   143  		} else {
   144  			prevV, ok := wb.prev[h]
   145  			if ok {
   146  				if v > prevV {
   147  					wb.prev[h] = 0
   148  					v -= prevV
   149  				} else {
   150  					wb.prev[h] = prevV - v
   151  					v = 0
   152  				}
   153  				p.tmpSample.tmpValues[vi] = v
   154  			}
   155  		}
   156  	}
   157  	if p.prev {
   158  		return false
   159  	}
   160  	return true
   161  }
   162  
   163  type writeBatchCache struct {
   164  	// sample type -> write batch
   165  	wbs []wbw
   166  
   167  	cumulative bool
   168  }
   169  
   170  type wbw struct {
   171  	wb        stackbuilder.WriteBatch
   172  	appName   string
   173  	appenders map[uint64]stackbuilder.SamplesAppender
   174  
   175  	// stackHash -> value for cumulative prev
   176  	prev map[uint64]int64
   177  }
   178  
   179  func (c *writeBatchCache) reset() {
   180  	for i := range c.wbs {
   181  		c.wbs[i].wb = nil
   182  		c.wbs[i].appenders = nil
   183  	}
   184  }
   185  
   186  func (c *writeBatchCache) getWriteBatch(parser *VTStreamingParser, sampleTypeIndex int) *wbw {
   187  	if sampleTypeIndex >= len(c.wbs) {
   188  		sz := sampleTypeIndex + 1
   189  		if sz < 4 {
   190  			sz = 4
   191  		}
   192  		newSampleTypes := arenahelper.MakeSlice[wbw](parser.arena, sz, sz)
   193  		copy(newSampleTypes, c.wbs)
   194  		c.wbs = newSampleTypes
   195  	}
   196  	p := &c.wbs[sampleTypeIndex]
   197  	if p.wb == nil {
   198  		appName, metadata := parser.getAppMetadata(sampleTypeIndex)
   199  		if appName == "" {
   200  			return nil
   201  		}
   202  		wb, err := parser.wbf.NewWriteBatch(appName, metadata)
   203  		if err != nil || wb == nil {
   204  			return nil
   205  		}
   206  		p.wb = wb
   207  		p.appName = appName
   208  		p.appenders = make(map[uint64]stackbuilder.SamplesAppender)
   209  		if c.cumulative {
   210  			p.prev = make(map[uint64]int64)
   211  		}
   212  	}
   213  	return p
   214  }
   215  
   216  func (c *writeBatchCache) flush() {
   217  	for i := range c.wbs {
   218  		wb := c.wbs[i].wb
   219  		if wb != nil {
   220  			wb.Flush()
   221  			c.wbs[i].wb = nil
   222  			c.wbs[i].appenders = nil
   223  		}
   224  	}
   225  }
   226  
   227  func (w *wbw) getAppender(parser *VTStreamingParser, labels Labels) stackbuilder.SamplesAppender {
   228  	h := labels.Hash()
   229  	e, found := w.appenders[h]
   230  	if found {
   231  		return e
   232  	}
   233  	allLabels := w.resolveLabels(parser, labels)
   234  	e = w.wb.SamplesAppender(parser.startTime.UnixNano(), parser.endTime.UnixNano(), allLabels)
   235  	w.appenders[h] = e
   236  	return e
   237  }
   238  
   239  func (w *wbw) resolveLabels(parser *VTStreamingParser, labels Labels) []stackbuilder.Label {
   240  	labelsSize := len(parser.labels) + len(labels)
   241  	allLabels := arenahelper.MakeSlice[stackbuilder.Label](parser.arena, 0, labelsSize)
   242  	for k, v := range parser.labels {
   243  		if k == "__name__" {
   244  			v = w.appName
   245  		}
   246  		allLabels = append(allLabels, stackbuilder.Label{Key: k, Value: v})
   247  	}
   248  	for _, label := range labels {
   249  		k := label >> 32
   250  		if k != 0 {
   251  			v := label & 0xffffffff
   252  			bk := parser.string(int64(k))
   253  			bv := parser.string(int64(v))
   254  			//sk := ""
   255  			//if len(bk) != 0 {
   256  			//	//sk = unsafe.String(&bk[0], len(bk))
   257  			//	sk = unsafe.String(&bk[0], len(bk))
   258  			//}
   259  			//sv := ""
   260  			//if len(bv) != 0 {
   261  			//	sv = unsafe.String(&bv[0], len(bv))
   262  			//}
   263  			sk := string(bk)
   264  			sv := string(bv)
   265  			allLabels = append(allLabels, stackbuilder.Label{Key: sk, Value: sv})
   266  		}
   267  	}
   268  	return allLabels
   269  }
   270  
   271  func (s *sample) hashStack(a arenahelper.ArenaWrapper) uint64 {
   272  	if len(s.tmpStack) == 0 {
   273  		return zeroHash
   274  	}
   275  	s.stackHashes = grow(a, s.stackHashes, len(s.tmpStack))
   276  	for _, frame := range s.tmpStack {
   277  		h := xxhash.Sum64(frame)
   278  		s.stackHashes = append(s.stackHashes, h)
   279  	}
   280  
   281  	var raw []byte
   282  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&raw))
   283  	sh.Data = uintptr(unsafe.Pointer(&s.stackHashes[0]))
   284  	sh.Len = len(s.stackHashes) * 8
   285  	sh.Cap = len(s.stackHashes) * 8
   286  	res := xxhash.Sum64(raw)
   287  	return res
   288  }