| // Copyright 2025 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package trace | |
| import ( | |
| "fmt" | |
| "internal/trace/tracev2" | |
| ) | |
| // timestamp is an unprocessed timestamp. | |
| type timestamp uint64 | |
| // batch represents a batch of trace events. | |
| // It is unparsed except for its header. | |
| type batch struct { | |
| m threadID | |
| time timestamp | |
| data []byte | |
| } | |
| // threadID is the runtime-internal M structure's ID. This is unique | |
| // for each OS thread. | |
| type threadID int64 | |
| // readBatch copies b and parses the trace batch header inside. | |
| // Returns the batch, the generation, bytes read, and an error. | |
| func readBatch(b []byte) (batch, uint64, uint64, error) { | |
| if len(b) == 0 { | |
| return batch{}, 0, 0, fmt.Errorf("batch is empty") | |
| } | |
| data := make([]byte, len(b)) | |
| if nw := copy(data, b); nw != len(b) { | |
| return batch{}, 0, 0, fmt.Errorf("unexpected error copying batch") | |
| } | |
| // Read batch header byte. | |
| if typ := tracev2.EventType(b[0]); typ != tracev2.EvEventBatch && typ != tracev2.EvExperimentalBatch { | |
| return batch{}, 0, 1, fmt.Errorf("expected batch event, got event %d", typ) | |
| } | |
| // Read the batch header: gen (generation), thread (M) ID, base timestamp | |
| // for the batch. | |
| total := 1 | |
| b = b[1:] | |
| gen, n, err := readUvarint(b) | |
| if err != nil { | |
| return batch{}, gen, uint64(total + n), fmt.Errorf("error reading batch gen: %w", err) | |
| } | |
| total += n | |
| b = b[n:] | |
| m, n, err := readUvarint(b) | |
| if err != nil { | |
| return batch{}, gen, uint64(total + n), fmt.Errorf("error reading batch M ID: %w", err) | |
| } | |
| total += n | |
| b = b[n:] | |
| ts, n, err := readUvarint(b) | |
| if err != nil { | |
| return batch{}, gen, uint64(total + n), fmt.Errorf("error reading batch timestamp: %w", err) | |
| } | |
| total += n | |
| b = b[n:] | |
| // Read in the size of the batch to follow. | |
| size, n, err := readUvarint(b) | |
| if err != nil { | |
| return batch{}, gen, uint64(total + n), fmt.Errorf("error reading batch size: %w", err) | |
| } | |
| if size > tracev2.MaxBatchSize { | |
| return batch{}, gen, uint64(total + n), fmt.Errorf("invalid batch size %d, maximum is %d", size, tracev2.MaxBatchSize) | |
| } | |
| total += n | |
| total += int(size) | |
| data = data[:total] | |
| // Return the batch. | |
| return batch{ | |
| m: threadID(m), | |
| time: timestamp(ts), | |
| data: data, | |
| }, gen, uint64(total), nil | |
| } |