aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/trace/v2/jsontrace_test.go2
-rw-r--r--src/cmd/trace/v2/main.go120
2 files changed, 113 insertions, 9 deletions
diff --git a/src/cmd/trace/v2/jsontrace_test.go b/src/cmd/trace/v2/jsontrace_test.go
index 65ce041c4fa..e1a53669b72 100644
--- a/src/cmd/trace/v2/jsontrace_test.go
+++ b/src/cmd/trace/v2/jsontrace_test.go
@@ -283,7 +283,7 @@ func getTestTrace(t *testing.T, testPath string) *parsedTrace {
}
// Parse the test trace.
- parsed, err := parseTrace(&trace)
+ parsed, err := parseTrace(&trace, int64(trace.Len()))
if err != nil {
t.Fatalf("failed to parse trace: %v", err)
}
diff --git a/src/cmd/trace/v2/main.go b/src/cmd/trace/v2/main.go
index f2a54eea907..93e9fa742c9 100644
--- a/src/cmd/trace/v2/main.go
+++ b/src/cmd/trace/v2/main.go
@@ -14,6 +14,8 @@ import (
"net"
"net/http"
"os"
+ "sync/atomic"
+ "time"
"internal/trace/v2/raw"
@@ -28,9 +30,16 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
}
defer tracef.Close()
+ // Get the size of the trace file.
+ fi, err := tracef.Stat()
+ if err != nil {
+ return fmt.Errorf("failed to stat trace file: %v", err)
+ }
+ traceSize := fi.Size()
+
// Handle requests for profiles.
if pprof != "" {
- parsed, err := parseTrace(tracef)
+ parsed, err := parseTrace(tracef, traceSize)
if err != nil {
return err
}
@@ -72,7 +81,7 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
addr := "http://" + ln.Addr().String()
log.Print("Preparing trace for viewer...")
- parsed, err := parseTrace(tracef)
+ parsed, err := parseTraceInteractive(tracef, traceSize)
if err != nil {
return err
}
@@ -80,6 +89,15 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
// We might double-close, but that's fine; we ignore the error.
tracef.Close()
+ // Print a nice message for a partial trace.
+ if parsed.err != nil {
+ log.Printf("Encountered error, but able to proceed. Error: %v", parsed.err)
+
+ lost := parsed.size - parsed.valid
+ pct := float64(lost) / float64(parsed.size) * 100
+ log.Printf("Lost %.2f%% of the latest trace data due to error (%s of %s)", pct, byteCount(lost), byteCount(parsed.size))
+ }
+
log.Print("Splitting trace for viewer...")
ranges, err := splitTrace(parsed)
if err != nil {
@@ -140,29 +158,79 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
return fmt.Errorf("failed to start http server: %w", err)
}
+func parseTraceInteractive(tr io.Reader, size int64) (parsed *parsedTrace, err error) {
+ done := make(chan struct{})
+ cr := countingReader{r: tr}
+ go func() {
+ parsed, err = parseTrace(&cr, size)
+ done <- struct{}{}
+ }()
+ ticker := time.NewTicker(5 * time.Second)
+progressLoop:
+ for {
+ select {
+ case <-ticker.C:
+ case <-done:
+ ticker.Stop()
+ break progressLoop
+ }
+ progress := cr.bytesRead.Load()
+ pct := float64(progress) / float64(size) * 100
+ log.Printf("%s of %s (%.1f%%) processed...", byteCount(progress), byteCount(size), pct)
+ }
+ return
+}
+
type parsedTrace struct {
- events []tracev2.Event
- summary *trace.Summary
+ events []tracev2.Event
+ summary *trace.Summary
+ size, valid int64
+ err error
}
-func parseTrace(tr io.Reader) (*parsedTrace, error) {
- r, err := tracev2.NewReader(tr)
+func parseTrace(rr io.Reader, size int64) (*parsedTrace, error) {
+ // Set up the reader.
+ cr := countingReader{r: rr}
+ r, err := tracev2.NewReader(&cr)
if err != nil {
return nil, fmt.Errorf("failed to create trace reader: %w", err)
}
+
+ // Set up state.
s := trace.NewSummarizer()
t := new(parsedTrace)
+ var validBytes int64
+ var validEvents int
for {
ev, err := r.ReadEvent()
if err == io.EOF {
+ validBytes = cr.bytesRead.Load()
+ validEvents = len(t.events)
+ break
+ }
+ if err != nil {
+ t.err = err
break
- } else if err != nil {
- return nil, fmt.Errorf("failed to read event: %w", err)
}
t.events = append(t.events, ev)
s.Event(&t.events[len(t.events)-1])
+
+ if ev.Kind() == tracev2.EventSync {
+ validBytes = cr.bytesRead.Load()
+ validEvents = len(t.events)
+ }
+ }
+
+ // Check to make sure we got at least one good generation.
+ if validEvents == 0 {
+ return nil, fmt.Errorf("failed to parse any useful part of the trace: %v", t.err)
}
+
+ // Finish off the parsedTrace.
t.summary = s.Finalize()
+ t.valid = validBytes
+ t.size = size
+ t.events = t.events[:validEvents]
return t, nil
}
@@ -217,3 +285,39 @@ func debugRawEvents(trace io.Reader) error {
fmt.Println(ev.String())
}
}
+
+type countingReader struct {
+ r io.Reader
+ bytesRead atomic.Int64
+}
+
+func (c *countingReader) Read(buf []byte) (n int, err error) {
+ n, err = c.r.Read(buf)
+ c.bytesRead.Add(int64(n))
+ return n, err
+}
+
+type byteCount int64
+
+func (b byteCount) String() string {
+ var suffix string
+ var divisor int64
+ switch {
+ case b < 1<<10:
+ suffix = "B"
+ divisor = 1
+ case b < 1<<20:
+ suffix = "KiB"
+ divisor = 1 << 10
+ case b < 1<<30:
+ suffix = "MiB"
+ divisor = 1 << 20
+ case b < 1<<40:
+ suffix = "GiB"
+ divisor = 1 << 30
+ }
+ if divisor == 1 {
+ return fmt.Sprintf("%d %s", b, suffix)
+ }
+ return fmt.Sprintf("%.1f %s", float64(b)/float64(divisor), suffix)
+}