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  }