1// Copyright 2023 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package trace 6 7import ( 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 "io" 12 13 "internal/trace/event" 14 "internal/trace/event/go122" 15) 16 17// timestamp is an unprocessed timestamp. 18type timestamp uint64 19 20// batch represents a batch of trace events. 21// It is unparsed except for its header. 22type batch struct { 23 m ThreadID 24 time timestamp 25 data []byte 26 exp event.Experiment 27} 28 29func (b *batch) isStringsBatch() bool { 30 return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStrings 31} 32 33func (b *batch) isStacksBatch() bool { 34 return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStacks 35} 36 37func (b *batch) isCPUSamplesBatch() bool { 38 return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvCPUSamples 39} 40 41func (b *batch) isFreqBatch() bool { 42 return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvFrequency 43} 44 45// readBatch reads the next full batch from r. 46func readBatch(r interface { 47 io.Reader 48 io.ByteReader 49}) (batch, uint64, error) { 50 // Read batch header byte. 51 b, err := r.ReadByte() 52 if err != nil { 53 return batch{}, 0, err 54 } 55 if typ := event.Type(b); typ != go122.EvEventBatch && typ != go122.EvExperimentalBatch { 56 return batch{}, 0, fmt.Errorf("expected batch event, got %s", go122.EventString(typ)) 57 } 58 59 // Read the experiment of we have one. 60 exp := event.NoExperiment 61 if event.Type(b) == go122.EvExperimentalBatch { 62 e, err := r.ReadByte() 63 if err != nil { 64 return batch{}, 0, err 65 } 66 exp = event.Experiment(e) 67 } 68 69 // Read the batch header: gen (generation), thread (M) ID, base timestamp 70 // for the batch. 71 gen, err := binary.ReadUvarint(r) 72 if err != nil { 73 return batch{}, gen, fmt.Errorf("error reading batch gen: %w", err) 74 } 75 m, err := binary.ReadUvarint(r) 76 if err != nil { 77 return batch{}, gen, fmt.Errorf("error reading batch M ID: %w", err) 78 } 79 ts, err := binary.ReadUvarint(r) 80 if err != nil { 81 return batch{}, gen, fmt.Errorf("error reading batch timestamp: %w", err) 82 } 83 84 // Read in the size of the batch to follow. 85 size, err := binary.ReadUvarint(r) 86 if err != nil { 87 return batch{}, gen, fmt.Errorf("error reading batch size: %w", err) 88 } 89 if size > go122.MaxBatchSize { 90 return batch{}, gen, fmt.Errorf("invalid batch size %d, maximum is %d", size, go122.MaxBatchSize) 91 } 92 93 // Copy out the batch for later processing. 94 var data bytes.Buffer 95 data.Grow(int(size)) 96 n, err := io.CopyN(&data, r, int64(size)) 97 if n != int64(size) { 98 return batch{}, gen, fmt.Errorf("failed to read full batch: read %d but wanted %d", n, size) 99 } 100 if err != nil { 101 return batch{}, gen, fmt.Errorf("copying batch data: %w", err) 102 } 103 104 // Return the batch. 105 return batch{ 106 m: ThreadID(m), 107 time: timestamp(ts), 108 data: data.Bytes(), 109 exp: exp, 110 }, gen, nil 111} 112