1
2
3
4
5 package raw
6
7 import (
8 "bufio"
9 "fmt"
10 "io"
11 "strconv"
12 "strings"
13 "unicode"
14
15 "internal/trace/event"
16 "internal/trace/version"
17 )
18
19
20
21 type TextReader struct {
22 v version.Version
23 specs []event.Spec
24 names map[string]event.Type
25 s *bufio.Scanner
26 }
27
28
29 func NewTextReader(r io.Reader) (*TextReader, error) {
30 tr := &TextReader{s: bufio.NewScanner(r)}
31 line, err := tr.nextLine()
32 if err != nil {
33 return nil, err
34 }
35 trace, line := readToken(line)
36 if trace != "Trace" {
37 return nil, fmt.Errorf("failed to parse header")
38 }
39 gover, line := readToken(line)
40 if !strings.HasPrefix(gover, "Go1.") {
41 return nil, fmt.Errorf("failed to parse header Go version")
42 }
43 rawv, err := strconv.ParseUint(gover[len("Go1."):], 10, 64)
44 if err != nil {
45 return nil, fmt.Errorf("failed to parse header Go version: %v", err)
46 }
47 v := version.Version(rawv)
48 if !v.Valid() {
49 return nil, fmt.Errorf("unknown or unsupported Go version 1.%d", v)
50 }
51 tr.v = v
52 tr.specs = v.Specs()
53 tr.names = event.Names(tr.specs)
54 for _, r := range line {
55 if !unicode.IsSpace(r) {
56 return nil, fmt.Errorf("encountered unexpected non-space at the end of the header: %q", line)
57 }
58 }
59 return tr, nil
60 }
61
62
63 func (r *TextReader) Version() version.Version {
64 return r.v
65 }
66
67
68 func (r *TextReader) ReadEvent() (Event, error) {
69 line, err := r.nextLine()
70 if err != nil {
71 return Event{}, err
72 }
73 evStr, line := readToken(line)
74 ev, ok := r.names[evStr]
75 if !ok {
76 return Event{}, fmt.Errorf("unidentified event: %s", evStr)
77 }
78 spec := r.specs[ev]
79 args, err := readArgs(line, spec.Args)
80 if err != nil {
81 return Event{}, fmt.Errorf("reading args for %s: %v", evStr, err)
82 }
83 if spec.IsStack {
84 len := int(args[1])
85 for i := 0; i < len; i++ {
86 line, err := r.nextLine()
87 if err == io.EOF {
88 return Event{}, fmt.Errorf("unexpected EOF while reading stack: args=%v", args)
89 }
90 if err != nil {
91 return Event{}, err
92 }
93 frame, err := readArgs(line, frameFields)
94 if err != nil {
95 return Event{}, err
96 }
97 args = append(args, frame...)
98 }
99 }
100 var data []byte
101 if spec.HasData {
102 line, err := r.nextLine()
103 if err == io.EOF {
104 return Event{}, fmt.Errorf("unexpected EOF while reading data for %s: args=%v", evStr, args)
105 }
106 if err != nil {
107 return Event{}, err
108 }
109 data, err = readData(line)
110 if err != nil {
111 return Event{}, err
112 }
113 }
114 return Event{
115 Version: r.v,
116 Ev: ev,
117 Args: args,
118 Data: data,
119 }, nil
120 }
121
122 func (r *TextReader) nextLine() (string, error) {
123 for {
124 if !r.s.Scan() {
125 if err := r.s.Err(); err != nil {
126 return "", err
127 }
128 return "", io.EOF
129 }
130 txt := r.s.Text()
131 tok, _ := readToken(txt)
132 if tok == "" {
133 continue
134 }
135 return txt, nil
136 }
137 }
138
139 var frameFields = []string{"pc", "func", "file", "line"}
140
141 func readArgs(s string, names []string) ([]uint64, error) {
142 var args []uint64
143 for _, name := range names {
144 arg, value, rest, err := readArg(s)
145 if err != nil {
146 return nil, err
147 }
148 if arg != name {
149 return nil, fmt.Errorf("expected argument %q, but got %q", name, arg)
150 }
151 args = append(args, value)
152 s = rest
153 }
154 for _, r := range s {
155 if !unicode.IsSpace(r) {
156 return nil, fmt.Errorf("encountered unexpected non-space at the end of an event: %q", s)
157 }
158 }
159 return args, nil
160 }
161
162 func readArg(s string) (arg string, value uint64, rest string, err error) {
163 var tok string
164 tok, rest = readToken(s)
165 if len(tok) == 0 {
166 return "", 0, s, fmt.Errorf("no argument")
167 }
168 parts := strings.SplitN(tok, "=", 2)
169 if len(parts) < 2 {
170 return "", 0, s, fmt.Errorf("malformed argument: %q", tok)
171 }
172 arg = parts[0]
173 value, err = strconv.ParseUint(parts[1], 10, 64)
174 if err != nil {
175 return arg, value, s, fmt.Errorf("failed to parse argument value %q for arg %q", parts[1], parts[0])
176 }
177 return
178 }
179
180 func readToken(s string) (token, rest string) {
181 tkStart := -1
182 for i, r := range s {
183 if r == '#' {
184 return "", ""
185 }
186 if !unicode.IsSpace(r) {
187 tkStart = i
188 break
189 }
190 }
191 if tkStart < 0 {
192 return "", ""
193 }
194 tkEnd := -1
195 for i, r := range s[tkStart:] {
196 if unicode.IsSpace(r) || r == '#' {
197 tkEnd = i + tkStart
198 break
199 }
200 }
201 if tkEnd < 0 {
202 return s[tkStart:], ""
203 }
204 return s[tkStart:tkEnd], s[tkEnd:]
205 }
206
207 func readData(line string) ([]byte, error) {
208 parts := strings.SplitN(line, "=", 2)
209 if len(parts) < 2 || strings.TrimSpace(parts[0]) != "data" {
210 return nil, fmt.Errorf("malformed data: %q", line)
211 }
212 data, err := strconv.Unquote(strings.TrimSpace(parts[1]))
213 if err != nil {
214 return nil, fmt.Errorf("failed to parse data: %q: %v", line, err)
215 }
216 return []byte(data), nil
217 }
218
View as plain text