writer.mx raw

   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  
   5  package raw
   6  
   7  import (
   8  	"encoding/binary"
   9  	"fmt"
  10  	"io"
  11  
  12  	"internal/trace/tracev2"
  13  	"internal/trace/version"
  14  )
  15  
  16  // Writer emits the wire format of a trace.
  17  //
  18  // It may not produce a byte-for-byte compatible trace from what is
  19  // produced by the runtime, because it may be missing extra padding
  20  // in the LEB128 encoding that the runtime adds but isn't necessary
  21  // when you know the data up-front.
  22  type Writer struct {
  23  	w     io.Writer
  24  	buf   []byte
  25  	v     version.Version
  26  	specs []tracev2.EventSpec
  27  }
  28  
  29  // NewWriter creates a new byte format writer.
  30  func NewWriter(w io.Writer, v version.Version) (*Writer, error) {
  31  	_, err := version.WriteHeader(w, v)
  32  	return &Writer{w: w, v: v, specs: v.Specs()}, err
  33  }
  34  
  35  // WriteEvent writes a single event to the trace wire format stream.
  36  func (w *Writer) WriteEvent(e Event) error {
  37  	// Check version.
  38  	if e.Version != w.v {
  39  		return fmt.Errorf("mismatched version between writer (go 1.%d) and event (go 1.%d)", w.v, e.Version)
  40  	}
  41  
  42  	// Write event header byte.
  43  	w.buf = append(w.buf, uint8(e.Ev))
  44  
  45  	// Write out all arguments.
  46  	spec := w.specs[e.Ev]
  47  	for _, arg := range e.Args[:len(spec.Args)] {
  48  		w.buf = binary.AppendUvarint(w.buf, arg)
  49  	}
  50  	if spec.IsStack {
  51  		frameArgs := e.Args[len(spec.Args):]
  52  		for i := 0; i < len(frameArgs); i++ {
  53  			w.buf = binary.AppendUvarint(w.buf, frameArgs[i])
  54  		}
  55  	}
  56  
  57  	// Write out the length of the data.
  58  	if spec.HasData {
  59  		w.buf = binary.AppendUvarint(w.buf, uint64(len(e.Data)))
  60  	}
  61  
  62  	// Write out varint events.
  63  	_, err := w.w.Write(w.buf)
  64  	w.buf = w.buf[:0]
  65  	if err != nil {
  66  		return err
  67  	}
  68  
  69  	// Write out data.
  70  	if spec.HasData {
  71  		_, err := w.w.Write(e.Data)
  72  		return err
  73  	}
  74  	return nil
  75  }
  76