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 main 6 7import ( 8 "cmp" 9 "encoding/binary" 10 "flag" 11 "fmt" 12 "io" 13 "log" 14 "os" 15 "slices" 16 "text/tabwriter" 17 18 "internal/trace/event" 19 "internal/trace/raw" 20) 21 22func init() { 23 flag.Usage = func() { 24 fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0]) 25 fmt.Fprintf(flag.CommandLine.Output(), "\n") 26 fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin.\n") 27 fmt.Fprintf(flag.CommandLine.Output(), "\n") 28 fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:") 29 fmt.Fprintf(flag.CommandLine.Output(), "\n") 30 fmt.Fprintf(flag.CommandLine.Output(), "* size - dumps size stats\n") 31 fmt.Fprintf(flag.CommandLine.Output(), "\n") 32 flag.PrintDefaults() 33 } 34 log.SetFlags(0) 35} 36 37func main() { 38 log.SetPrefix("") 39 flag.Parse() 40 41 if flag.NArg() != 1 { 42 log.Print("missing mode argument") 43 flag.Usage() 44 os.Exit(1) 45 } 46 var err error 47 switch mode := flag.Arg(0); mode { 48 case "size": 49 err = printSizeStats(os.Stdin) 50 default: 51 log.Printf("unknown mode %q", mode) 52 flag.Usage() 53 os.Exit(1) 54 } 55 if err != nil { 56 log.Fatalf("error: %v", err) 57 os.Exit(1) 58 } 59} 60 61func printSizeStats(r io.Reader) error { 62 cr := countingReader{Reader: r} 63 tr, err := raw.NewReader(&cr) 64 if err != nil { 65 return err 66 } 67 type eventStats struct { 68 typ event.Type 69 count int 70 bytes int 71 } 72 var stats [256]eventStats 73 for i := range stats { 74 stats[i].typ = event.Type(i) 75 } 76 eventsRead := 0 77 for { 78 e, err := tr.ReadEvent() 79 if err == io.EOF { 80 break 81 } 82 if err != nil { 83 return err 84 } 85 s := &stats[e.Ev] 86 s.count++ 87 s.bytes += encodedSize(&e) 88 eventsRead++ 89 } 90 slices.SortFunc(stats[:], func(a, b eventStats) int { 91 return cmp.Compare(b.bytes, a.bytes) 92 }) 93 specs := tr.Version().Specs() 94 w := tabwriter.NewWriter(os.Stdout, 3, 8, 2, ' ', 0) 95 fmt.Fprintf(w, "Event\tBytes\t%%\tCount\t%%\n") 96 fmt.Fprintf(w, "-\t-\t-\t-\t-\n") 97 for i := range stats { 98 stat := &stats[i] 99 name := "" 100 if int(stat.typ) >= len(specs) { 101 name = fmt.Sprintf("<unknown (%d)>", stat.typ) 102 } else { 103 name = specs[stat.typ].Name 104 } 105 bytesPct := float64(stat.bytes) / float64(cr.bytesRead) * 100 106 countPct := float64(stat.count) / float64(eventsRead) * 100 107 fmt.Fprintf(w, "%s\t%d\t%.2f%%\t%d\t%.2f%%\n", name, stat.bytes, bytesPct, stat.count, countPct) 108 } 109 w.Flush() 110 return nil 111} 112 113func encodedSize(e *raw.Event) int { 114 size := 1 115 var buf [binary.MaxVarintLen64]byte 116 for _, arg := range e.Args { 117 size += binary.PutUvarint(buf[:], arg) 118 } 119 spec := e.Version.Specs()[e.Ev] 120 if spec.HasData { 121 size += binary.PutUvarint(buf[:], uint64(len(e.Data))) 122 size += len(e.Data) 123 } 124 return size 125} 126 127type countingReader struct { 128 io.Reader 129 bytesRead int 130} 131 132func (r *countingReader) Read(b []byte) (int, error) { 133 n, err := r.Reader.Read(b) 134 r.bytesRead += n 135 return n, err 136} 137