Skip to content

Commit d1dcffd

Browse files
committed
cmd/trace/v2: add support for pprof endpoints
This change adds support for the pprof endpoints to cmd/trace/v2. In the process, I realized we need to pass the goroutine summaries to more places, and previous CLs had already done the goroutine analysis during cmd/trace startup. This change thus refactors the goroutine analysis API once again to operate in a streaming manner, and to run at the same time as the initial trace parsing. Now we can include it in the parsedTrace type and pass that around as the de-facto global trace context. Note: for simplicity, this change redefines "syscall" profiles to capture *all* syscalls, not just syscalls that block. IIUC, this choice was partly the result of a limitation in the previous trace format that syscalls don't all have complete durations and many short syscalls are treated as instant. To this end, this change modifies the text on the main trace webpage to reflect this change. For #60773. For #63960. Change-Id: I601d9250ab0849a0bfaef233fd9b1e81aca9a22a Reviewed-on: https://go-review.googlesource.com/c/go/+/541999 Auto-Submit: Michael Knyszek <[email protected]> Reviewed-by: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent b839348 commit d1dcffd

File tree

6 files changed

+532
-71
lines changed

6 files changed

+532
-71
lines changed

src/cmd/trace/v2/goroutines.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ Table of contents
346346
<td> <a href="/block?id={{.PC}}">graph</a> <a href="/block?id={{.PC}}&raw=1" download="block.profile">(download)</a></td>
347347
</tr>
348348
<tr>
349-
<td>Syscall block profile:</td>
349+
<td>Syscall profile:</td>
350350
<td> <a href="/syscall?id={{.PC}}">graph</a> <a href="/syscall?id={{.PC}}&raw=1" download="syscall.profile">(download)</a></td>
351351
</tr>
352352
<tr>

src/cmd/trace/v2/main.go

+39-9
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
5656
if err != nil {
5757
return err
5858
}
59-
log.Printf("Analyzing goroutines...")
60-
gSummaries := trace.SummarizeGoroutines(parsed.events)
6159

6260
log.Printf("Opening browser. Trace viewer is listening on %s", addr)
6361
browser.Open(addr)
@@ -67,28 +65,50 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
6765
}
6866

6967
mux := http.NewServeMux()
68+
69+
// Main endpoint.
7070
mux.Handle("/", traceviewer.MainHandler(ranges))
71+
72+
// Catapult handlers.
7173
mux.Handle("/trace", traceviewer.TraceHandler())
7274
mux.Handle("/jsontrace", JSONTraceHandler(parsed))
7375
mux.Handle("/static/", traceviewer.StaticHandler())
74-
mux.HandleFunc("/goroutines", GoroutinesHandlerFunc(gSummaries))
75-
mux.HandleFunc("/goroutine", GoroutineHandler(gSummaries))
76+
77+
// Goroutines handlers.
78+
mux.HandleFunc("/goroutines", GoroutinesHandlerFunc(parsed.gSummaries))
79+
mux.HandleFunc("/goroutine", GoroutineHandler(parsed.gSummaries))
80+
81+
// MMU handler.
7682
mux.HandleFunc("/mmu", traceviewer.MMUHandlerFunc(ranges, mutatorUtil))
7783

84+
// Basic pprof endpoints.
85+
mux.HandleFunc("/io", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofIO(), parsed)))
86+
mux.HandleFunc("/block", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofBlock(), parsed)))
87+
mux.HandleFunc("/syscall", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSyscall(), parsed)))
88+
mux.HandleFunc("/sched", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSched(), parsed)))
89+
90+
// Region-based pprof endpoints.
91+
mux.HandleFunc("/regionio", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofIO(), parsed)))
92+
mux.HandleFunc("/regionblock", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofBlock(), parsed)))
93+
mux.HandleFunc("/regionsyscall", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSyscall(), parsed)))
94+
mux.HandleFunc("/regionsched", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSched(), parsed)))
95+
7896
err = http.Serve(ln, mux)
7997
return fmt.Errorf("failed to start http server: %w", err)
8098
}
8199

82100
type parsedTrace struct {
83-
events []tracev2.Event
101+
events []tracev2.Event
102+
gSummaries map[tracev2.GoID]*trace.GoroutineSummary
84103
}
85104

86-
func parseTrace(trace io.Reader) (*parsedTrace, error) {
87-
r, err := tracev2.NewReader(trace)
105+
func parseTrace(tr io.Reader) (*parsedTrace, error) {
106+
r, err := tracev2.NewReader(tr)
88107
if err != nil {
89108
return nil, fmt.Errorf("failed to create trace reader: %w", err)
90109
}
91-
var t parsedTrace
110+
s := trace.NewGoroutineSummarizer()
111+
t := new(parsedTrace)
92112
for {
93113
ev, err := r.ReadEvent()
94114
if err == io.EOF {
@@ -97,8 +117,18 @@ func parseTrace(trace io.Reader) (*parsedTrace, error) {
97117
return nil, fmt.Errorf("failed to read event: %w", err)
98118
}
99119
t.events = append(t.events, ev)
120+
s.Event(&t.events[len(t.events)-1])
100121
}
101-
return &t, nil
122+
t.gSummaries = s.Finalize()
123+
return t, nil
124+
}
125+
126+
func (t *parsedTrace) startTime() tracev2.Time {
127+
return t.events[0].Time()
128+
}
129+
130+
func (t *parsedTrace) endTime() tracev2.Time {
131+
return t.events[len(t.events)-1].Time()
102132
}
103133

104134
// splitTrace splits the trace into a number of ranges, each resulting in approx 100 MiB of

0 commit comments

Comments
 (0)