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 }