// Copyright 2023 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 raw import ( "bufio" "encoding/binary" "fmt" "io" "internal/trace/v2/event" "internal/trace/v2/version" ) // Reader parses trace bytes with only very basic validation // into an event stream. type Reader struct { r *bufio.Reader v version.Version specs []event.Spec } // NewReader creates a new reader for the trace wire format. func NewReader(r io.Reader) (*Reader, error) { br := bufio.NewReader(r) v, err := version.ReadHeader(br) if err != nil { return nil, err } return &Reader{r: br, v: v, specs: v.Specs()}, nil } // Version returns the version of the trace that we're reading. func (r *Reader) Version() version.Version { return r.v } // ReadEvent reads and returns the next trace event in the byte stream. func (r *Reader) ReadEvent() (Event, error) { evb, err := r.r.ReadByte() if err == io.EOF { return Event{}, io.EOF } if err != nil { return Event{}, err } if int(evb) >= len(r.specs) || evb == 0 { return Event{}, fmt.Errorf("invalid event type: %d", evb) } ev := event.Type(evb) spec := r.specs[ev] args, err := r.readArgs(len(spec.Args)) if err != nil { return Event{}, err } if spec.IsStack { len := int(args[1]) for i := 0; i < len; i++ { // Each stack frame has four args: pc, func ID, file ID, line number. frame, err := r.readArgs(4) if err != nil { return Event{}, err } args = append(args, frame...) } } var data []byte if spec.HasData { data, err = r.readData() if err != nil { return Event{}, err } } return Event{ Version: r.v, Ev: ev, Args: args, Data: data, }, nil } func (r *Reader) readArgs(n int) ([]uint64, error) { var args []uint64 for i := 0; i < n; i++ { val, err := binary.ReadUvarint(r.r) if err != nil { return nil, err } args = append(args, val) } return args, nil } func (r *Reader) readData() ([]byte, error) { len, err := binary.ReadUvarint(r.r) if err != nil { return nil, err } var data []byte for i := 0; i < int(len); i++ { b, err := r.r.ReadByte() if err != nil { return nil, err } data = append(data, b) } return data, nil }