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
5package raw
6
7import (
8	"encoding/binary"
9	"fmt"
10	"io"
11
12	"internal/trace/event"
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.
22type Writer struct {
23	w     io.Writer
24	buf   []byte
25	v     version.Version
26	specs []event.Spec
27}
28
29// NewWriter creates a new byte format writer.
30func 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.
36func (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