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:
parent
b8f83e2270
commit
5f073d361f
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user