1
0
mirror of https://github.com/golang/go synced 2024-11-26 14:46:47 -07:00

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 <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
Michael Anthony Knyszek 2024-06-21 15:32:29 +00:00 committed by Gopher Robot
parent b8f83e2270
commit 5f073d361f
5 changed files with 85 additions and 282 deletions

View File

@ -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("<unknown (%d)>", 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

View File

@ -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
}

View File

@ -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("<unknown (%d)>", 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
}

View File

@ -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
}

View File

@ -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)
}
}
}