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