| // 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/event" |
| "internal/trace/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 |
| } |