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.
17 "internal/trace/v2/event"
18 "internal/trace/v2/event/go122"
21 // generation contains all the trace data for a single
22 // trace generation. It is purely data: it does not
23 // track any parse state nor does it contain a cursor
24 // into the generation.
25 type generation struct {
27 batches map[ThreadID][]batch
28 cpuSamples []cpuSample
32 // spilledBatch represents a batch that was read out for the next generation,
33 // while reading the previous one. It's passed on when parsing the next
35 type spilledBatch struct {
40 // readGeneration buffers and decodes the structural elements of a trace generation
41 // out of r. spill is the first batch of the new generation (already buffered and
42 // parsed from reading the last generation). Returns the generation and the first
43 // batch read of the next generation, if any.
44 func readGeneration(r *bufio.Reader, spill *spilledBatch) (*generation, *spilledBatch, error) {
46 evTable: new(evTable),
47 batches: make(map[ThreadID][]batch),
49 // Process the spilled batch.
52 if err := processBatch(g, *spill.batch); err != nil {
57 // Read batches one at a time until we either hit EOF or
58 // the next generation.
60 b, gen, err := readBatch(r)
68 // 0 is a sentinel used by the runtime, so we'll never see it.
69 return nil, nil, fmt.Errorf("invalid generation number %d", gen)
75 if gen == g.gen+1 { // TODO: advance this the same way the runtime does.
76 spill = &spilledBatch{gen: gen, batch: &b}
80 // N.B. Fail as fast as possible if we see this. At first it
81 // may seem prudent to be fault-tolerant and assume we have a
82 // complete generation, parsing and returning that first. However,
83 // if the batches are mixed across generations then it's likely
84 // we won't be able to parse this generation correctly at all.
85 // Rather than return a cryptic error in that case, indicate the
86 // problem as soon as we see it.
87 return nil, nil, fmt.Errorf("generations out of order")
89 if err := processBatch(g, b); err != nil {
94 // Check some invariants.
96 return nil, nil, fmt.Errorf("no frequency event found")
98 // N.B. Trust that the batch order is correct. We can't validate the batch order
99 // by timestamp because the timestamps could just be plain wrong. The source of
100 // truth is the order things appear in the trace and the partial order sequence
101 // numbers on certain events. If it turns out the batch order is actually incorrect
102 // we'll very likely fail to advance a partial order from the frontier.
104 // Compactify stacks and strings for better lookup performance later.
105 g.stacks.compactify()
106 g.strings.compactify()
109 if err := validateStackStrings(&g.stacks, &g.strings); err != nil {
113 // Fix up the CPU sample timestamps, now that we have freq.
114 for i := range g.cpuSamples {
115 s := &g.cpuSamples[i]
116 s.time = g.freq.mul(timestamp(s.time))
118 // Sort the CPU samples.
119 slices.SortFunc(g.cpuSamples, func(a, b cpuSample) int {
120 return cmp.Compare(a.time, b.time)
125 // processBatch adds the batch to the generation.
126 func processBatch(g *generation, b batch) error {
128 case b.isStringsBatch():
129 if err := addStrings(&g.strings, b); err != nil {
132 case b.isStacksBatch():
133 if err := addStacks(&g.stacks, b); err != nil {
136 case b.isCPUSamplesBatch():
137 samples, err := addCPUSamples(g.cpuSamples, b)
141 g.cpuSamples = samples
142 case b.isFreqBatch():
143 freq, err := parseFreq(b)
148 return fmt.Errorf("found multiple frequency events")
152 g.batches[b.m] = append(g.batches[b.m], b)
157 // validateStackStrings makes sure all the string references in
158 // the stack table are present in the string table.
159 func validateStackStrings(stacks *dataTable[stackID, stack], strings *dataTable[stringID, string]) error {
161 stacks.forEach(func(id stackID, stk stack) bool {
162 for _, frame := range stk.frames {
163 _, ok := strings.get(frame.funcID)
165 err = fmt.Errorf("found invalid func string ID %d for stack %d", frame.funcID, id)
168 _, ok = strings.get(frame.fileID)
170 err = fmt.Errorf("found invalid file string ID %d for stack %d", frame.fileID, id)
179 // addStrings takes a batch whose first byte is an EvStrings event
180 // (indicating that the batch contains only strings) and adds each
181 // string contained therein to the provided strings map.
182 func addStrings(stringTable *dataTable[stringID, string], b batch) error {
183 if !b.isStringsBatch() {
184 return fmt.Errorf("internal error: addStrings called on non-string batch")
186 r := bytes.NewReader(b.data)
187 hdr, err := r.ReadByte() // Consume the EvStrings byte.
188 if err != nil || event.Type(hdr) != go122.EvStrings {
189 return fmt.Errorf("missing strings batch header")
192 var sb strings.Builder
195 ev, err := r.ReadByte()
199 if event.Type(ev) != go122.EvString {
200 return fmt.Errorf("expected string event, got %d", ev)
203 // Read the string's ID.
204 id, err := binary.ReadUvarint(r)
209 // Read the string's length.
210 len, err := binary.ReadUvarint(r)
214 if len > go122.MaxStringSize {
215 return fmt.Errorf("invalid string size %d, maximum is %d", len, go122.MaxStringSize)
218 // Copy out the string.
219 n, err := io.CopyN(&sb, r, int64(len))
221 return fmt.Errorf("failed to read full string: read %d but wanted %d", n, len)
224 return fmt.Errorf("copying string data: %w", err)
227 // Add the string to the map.
230 if err := stringTable.insert(stringID(id), s); err != nil {
237 // addStacks takes a batch whose first byte is an EvStacks event
238 // (indicating that the batch contains only stacks) and adds each
239 // string contained therein to the provided stacks map.
240 func addStacks(stackTable *dataTable[stackID, stack], b batch) error {
241 if !b.isStacksBatch() {
242 return fmt.Errorf("internal error: addStacks called on non-stacks batch")
244 r := bytes.NewReader(b.data)
245 hdr, err := r.ReadByte() // Consume the EvStacks byte.
246 if err != nil || event.Type(hdr) != go122.EvStacks {
247 return fmt.Errorf("missing stacks batch header")
252 ev, err := r.ReadByte()
256 if event.Type(ev) != go122.EvStack {
257 return fmt.Errorf("expected stack event, got %d", ev)
260 // Read the stack's ID.
261 id, err := binary.ReadUvarint(r)
266 // Read how many frames are in each stack.
267 nFrames, err := binary.ReadUvarint(r)
271 if nFrames > go122.MaxFramesPerStack {
272 return fmt.Errorf("invalid stack size %d, maximum is %d", nFrames, go122.MaxFramesPerStack)
275 // Each frame consists of 4 fields: pc, funcID (string), fileID (string), line.
276 frames := make([]frame, 0, nFrames)
277 for i := uint64(0); i < nFrames; i++ {
278 // Read the frame data.
279 pc, err := binary.ReadUvarint(r)
281 return fmt.Errorf("reading frame %d's PC for stack %d: %w", i+1, id, err)
283 funcID, err := binary.ReadUvarint(r)
285 return fmt.Errorf("reading frame %d's funcID for stack %d: %w", i+1, id, err)
287 fileID, err := binary.ReadUvarint(r)
289 return fmt.Errorf("reading frame %d's fileID for stack %d: %w", i+1, id, err)
291 line, err := binary.ReadUvarint(r)
293 return fmt.Errorf("reading frame %d's line for stack %d: %w", i+1, id, err)
295 frames = append(frames, frame{
297 funcID: stringID(funcID),
298 fileID: stringID(fileID),
303 // Add the stack to the map.
304 if err := stackTable.insert(stackID(id), stack{frames: frames}); err != nil {
311 // addCPUSamples takes a batch whose first byte is an EvCPUSamples event
312 // (indicating that the batch contains only CPU samples) and adds each
313 // sample contained therein to the provided samples list.
314 func addCPUSamples(samples []cpuSample, b batch) ([]cpuSample, error) {
315 if !b.isCPUSamplesBatch() {
316 return nil, fmt.Errorf("internal error: addStrings called on non-string batch")
318 r := bytes.NewReader(b.data)
319 hdr, err := r.ReadByte() // Consume the EvCPUSamples byte.
320 if err != nil || event.Type(hdr) != go122.EvCPUSamples {
321 return nil, fmt.Errorf("missing CPU samples batch header")
326 ev, err := r.ReadByte()
330 if event.Type(ev) != go122.EvCPUSample {
331 return nil, fmt.Errorf("expected CPU sample event, got %d", ev)
334 // Read the sample's timestamp.
335 ts, err := binary.ReadUvarint(r)
340 // Read the sample's M.
341 m, err := binary.ReadUvarint(r)
347 // Read the sample's P.
348 p, err := binary.ReadUvarint(r)
354 // Read the sample's G.
355 g, err := binary.ReadUvarint(r)
364 // Read the sample's stack.
365 s, err := binary.ReadUvarint(r)
370 // Add the sample to the slice.
371 samples = append(samples, cpuSample{
377 time: Time(ts), // N.B. this is really a "timestamp," not a Time.
384 // parseFreq parses out a lone EvFrequency from a batch.
385 func parseFreq(b batch) (frequency, error) {
386 if !b.isFreqBatch() {
387 return 0, fmt.Errorf("internal error: parseFreq called on non-frequency batch")
389 r := bytes.NewReader(b.data)
390 r.ReadByte() // Consume the EvFrequency byte.
392 // Read the frequency. It'll come out as timestamp units per second.
393 f, err := binary.ReadUvarint(r)
397 // Convert to nanoseconds per timestamp unit.
398 return frequency(1.0 / (float64(f) / 1e9)), nil