From: Michael Anthony Knyszek Date: Fri, 21 Jun 2024 15:32:29 +0000 (+0000) Subject: cmd/trace: merge testdata debugging tools into the trace tool X-Git-Tag: go1.24rc1~1432 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=5f073d361f96e38bd31cf9274769bd4d0f5e0a5a;p=gostls13.git cmd/trace: merge testdata debugging tools into the trace tool Currently internal/trace/testdata contains three debugging tools which were written early in the trace rewrite for debugging. Two of these are completely redundant with go tool trace -d=1 and go tool trace -d=2. The only remaining one landed in the last cycle and could easily also be another debug mode. This change thus merges gotraceeventstats into go tool trace as a new debug mode, and updates the debug mode flag (-d) to accept a string, giving each mode a more descriptive name. Change-Id: I170f30440691b81de846b4e247deb3d0982fc205 Reviewed-on: https://go-review.googlesource.com/c/go/+/593975 Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI Auto-Submit: Michael Knyszek --- diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index d51ee58164..075212eacb 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -7,9 +7,11 @@ package main import ( "cmd/internal/browser" "cmd/internal/telemetry/counter" + "cmp" "flag" "fmt" "internal/trace" + "internal/trace/event" "internal/trace/raw" "internal/trace/traceviewer" "io" @@ -18,7 +20,9 @@ import ( "net/http" _ "net/http/pprof" // Required to use pprof "os" + "slices" "sync/atomic" + "text/tabwriter" "time" ) @@ -45,7 +49,7 @@ Supported profile types are: Flags: -http=addr: HTTP service address (e.g., ':6060') -pprof=type: print a pprof-like profile instead - -d=int: print debug info such as parsed events (1 for high-level, 2 for low-level) + -d=mode: print debug info and exit (modes: wire, parsed, footprint) Note that while the various profiles available when launching 'go tool trace' work on every browser, the trace viewer itself @@ -56,7 +60,7 @@ and is only actively tested on that browser. var ( httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')") pprofFlag = flag.String("pprof", "", "print a pprof-like profile instead") - debugFlag = flag.Int("d", 0, "print debug information (1 for basic debug info, 2 for lower-level info)") + debugFlag = flag.String("d", "", "print debug info and exit (modes: wire, parsed, footprint)") // The binary file name, left here for serveSVGProfile. programBinary string @@ -128,11 +132,17 @@ func main() { } // Debug flags. - switch *debugFlag { - case 1: - logAndDie(debugProcessedEvents(tracef)) - case 2: - logAndDie(debugRawEvents(tracef)) + if *debugFlag != "" { + switch *debugFlag { + case "parsed": + logAndDie(debugProcessedEvents(tracef)) + case "wire": + logAndDie(debugRawEvents(tracef)) + case "footprint": + logAndDie(debugEventsFootprint(tracef)) + default: + logAndDie(fmt.Errorf("invalid debug mode %s, want one of: parsed, wire, footprint", *debugFlag)) + } } ln, err := net.Listen("tcp", *httpFlag) @@ -355,6 +365,58 @@ func debugRawEvents(trc io.Reader) error { } } +func debugEventsFootprint(trc io.Reader) error { + cr := countingReader{r: trc} + 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 += e.EncodedSize() + 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.Load()) * 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 +} + type countingReader struct { r io.Reader bytesRead atomic.Int64 diff --git a/src/internal/trace/raw/event.go b/src/internal/trace/raw/event.go index 4766fbe563..e163a2c6ef 100644 --- a/src/internal/trace/raw/event.go +++ b/src/internal/trace/raw/event.go @@ -5,6 +5,7 @@ package raw import ( + "encoding/binary" "strconv" "strings" @@ -58,3 +59,18 @@ func (e *Event) String() string { } return s.String() } + +// EncodedSize returns the canonical encoded size of an event. +func (e *Event) EncodedSize() 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 +} diff --git a/src/internal/trace/testdata/cmd/gotraceeventstats/main.go b/src/internal/trace/testdata/cmd/gotraceeventstats/main.go deleted file mode 100644 index d2379daab5..0000000000 --- a/src/internal/trace/testdata/cmd/gotraceeventstats/main.go +++ /dev/null @@ -1,136 +0,0 @@ -// 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/event" - "internal/trace/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 -} diff --git a/src/internal/trace/testdata/cmd/gotraceraw/main.go b/src/internal/trace/testdata/cmd/gotraceraw/main.go deleted file mode 100644 index ec4ebf23b9..0000000000 --- a/src/internal/trace/testdata/cmd/gotraceraw/main.go +++ /dev/null @@ -1,86 +0,0 @@ -// 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 ( - "flag" - "fmt" - "io" - "log" - "os" - - "internal/trace/raw" - "internal/trace/version" -) - -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(), "Supported modes:") - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "* text2bytes - converts a text format trace to bytes\n") - fmt.Fprintf(flag.CommandLine.Output(), "* bytes2text - converts a byte format trace to text\n") - fmt.Fprintf(flag.CommandLine.Output(), "\n") - flag.PrintDefaults() - } - log.SetFlags(0) -} - -func main() { - flag.Parse() - if narg := flag.NArg(); narg != 1 { - log.Fatal("expected exactly one positional argument: the mode to operate in; see -h output") - } - - r := os.Stdin - w := os.Stdout - - var tr traceReader - var tw traceWriter - var err error - - switch flag.Arg(0) { - case "text2bytes": - tr, err = raw.NewTextReader(r) - if err != nil { - log.Fatal(err) - } - tw, err = raw.NewWriter(w, tr.Version()) - if err != nil { - log.Fatal(err) - } - case "bytes2text": - tr, err = raw.NewReader(r) - if err != nil { - log.Fatal(err) - } - tw, err = raw.NewTextWriter(w, tr.Version()) - if err != nil { - log.Fatal(err) - } - } - for { - ev, err := tr.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - log.Fatal(err) - } - if err := tw.WriteEvent(ev); err != nil { - log.Fatal(err) - } - } -} - -type traceReader interface { - Version() version.Version - ReadEvent() (raw.Event, error) -} - -type traceWriter interface { - WriteEvent(raw.Event) error -} diff --git a/src/internal/trace/testdata/cmd/gotracevalidate/main.go b/src/internal/trace/testdata/cmd/gotracevalidate/main.go deleted file mode 100644 index 6c681df179..0000000000 --- a/src/internal/trace/testdata/cmd/gotracevalidate/main.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 ( - "flag" - "fmt" - "io" - "log" - "os" - - "internal/trace" - "internal/trace/testtrace" -) - -func init() { - flag.Usage = func() { - fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s\n", os.Args[0]) - fmt.Fprintf(flag.CommandLine.Output(), "\n") - fmt.Fprintf(flag.CommandLine.Output(), "Accepts a trace at stdin and validates it.\n") - flag.PrintDefaults() - } - log.SetFlags(0) -} - -var logEvents = flag.Bool("log-events", false, "whether to log events") - -func main() { - flag.Parse() - - r, err := trace.NewReader(os.Stdin) - if err != nil { - log.Fatal(err) - } - v := testtrace.NewValidator() - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - log.Fatal(err) - } - if *logEvents { - log.Println(ev.String()) - } - if err := v.Event(ev); err != nil { - log.Fatal(err) - } - } -}