From: Michael Anthony Knyszek Date: Fri, 26 Apr 2024 19:53:22 +0000 (+0000) Subject: internal/trace/v2: add gotraceeventstats utility X-Git-Tag: go1.23rc1~427 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c3bd543cc304b405b00263e2d4e683f9b65214ec;p=gostls13.git internal/trace/v2: add gotraceeventstats utility Change-Id: Ibf83d74a76edf2fe2896fa1e7f93ab1296fc5d75 Reviewed-on: https://go-review.googlesource.com/c/go/+/583378 Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI Auto-Submit: Michael Knyszek --- diff --git a/src/internal/trace/v2/testdata/cmd/gotraceeventstats/main.go b/src/internal/trace/v2/testdata/cmd/gotraceeventstats/main.go new file mode 100644 index 0000000000..ad06af6481 --- /dev/null +++ b/src/internal/trace/v2/testdata/cmd/gotraceeventstats/main.go @@ -0,0 +1,136 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "cmp" + "encoding/binary" + "flag" + "fmt" + "io" + "log" + "os" + "slices" + "text/tabwriter" + + "internal/trace/v2/event" + "internal/trace/v2/raw" +) + +func init() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [mode]\n", os.Args[0]) + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin.\n") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "Supported modes:") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + fmt.Fprintf(flag.CommandLine.Output(), "* size - dumps size stats\n") + fmt.Fprintf(flag.CommandLine.Output(), "\n") + flag.PrintDefaults() + } + log.SetFlags(0) +} + +func main() { + log.SetPrefix("") + flag.Parse() + + if flag.NArg() != 1 { + log.Print("missing mode argument") + flag.Usage() + os.Exit(1) + } + var err error + switch mode := flag.Arg(0); mode { + case "size": + err = printSizeStats(os.Stdin) + default: + log.Printf("unknown mode %q", mode) + flag.Usage() + os.Exit(1) + } + if err != nil { + log.Fatalf("error: %v", err) + os.Exit(1) + } +} + +func printSizeStats(r io.Reader) error { + cr := countingReader{Reader: r} + tr, err := raw.NewReader(&cr) + if err != nil { + return err + } + type eventStats struct { + typ event.Type + count int + bytes int + } + var stats [256]eventStats + for i := range stats { + stats[i].typ = event.Type(i) + } + eventsRead := 0 + for { + e, err := tr.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + return err + } + s := &stats[e.Ev] + s.count++ + s.bytes += encodedSize(&e) + eventsRead++ + } + slices.SortFunc(stats[:], func(a, b eventStats) int { + return cmp.Compare(b.bytes, a.bytes) + }) + specs := tr.Version().Specs() + w := tabwriter.NewWriter(os.Stdout, 3, 8, 2, ' ', 0) + fmt.Fprintf(w, "Event\tBytes\t%%\tCount\t%%\n") + fmt.Fprintf(w, "-\t-\t-\t-\t-\n") + for i := range stats { + stat := &stats[i] + name := "" + if int(stat.typ) >= len(specs) { + name = fmt.Sprintf("", stat.typ) + } else { + name = specs[stat.typ].Name + } + bytesPct := float64(stat.bytes) / float64(cr.bytesRead) * 100 + countPct := float64(stat.count) / float64(eventsRead) * 100 + fmt.Fprintf(w, "%s\t%d\t%.2f%%\t%d\t%.2f%%\n", name, stat.bytes, bytesPct, stat.count, countPct) + } + w.Flush() + return nil +} + +func encodedSize(e *raw.Event) int { + size := 1 + var buf [binary.MaxVarintLen64]byte + for _, arg := range e.Args { + size += binary.PutUvarint(buf[:], arg) + } + spec := e.Version.Specs()[e.Ev] + if spec.HasData { + size += binary.PutUvarint(buf[:], uint64(len(e.Data))) + size += len(e.Data) + } + return size +} + +type countingReader struct { + io.Reader + bytesRead int +} + +func (r *countingReader) Read(b []byte) (int, error) { + n, err := r.Reader.Read(b) + r.bytesRead += n + return n, err +}