Skip to content

Commit 816ee0b

Browse files
mknyszekgopherbot
authored andcommitted
internal/trace: add task analysis for v2 traces
For v1 traces, cmd/trace contains code for analyzing tasks separately from the goroutine analysis code present in internal/trace. As I started to look into porting that code to v2 traces, I noticed that it wouldn't be too hard to just generalize the existing v2 goroutine summary code to generate exactly the same information. This change does exactly that. For #60773. For #63960. Change-Id: I0cdd9bf9ba11fb292a9ffc37dbf18c2a6a2483b8 Reviewed-on: https://go-review.googlesource.com/c/go/+/542076 Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
1 parent 5dde69f commit 816ee0b

File tree

6 files changed

+1554
-37
lines changed

6 files changed

+1554
-37
lines changed

src/cmd/trace/v2/main.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
7575
mux.Handle("/static/", traceviewer.StaticHandler())
7676

7777
// Goroutines handlers.
78-
mux.HandleFunc("/goroutines", GoroutinesHandlerFunc(parsed.gSummaries))
79-
mux.HandleFunc("/goroutine", GoroutineHandler(parsed.gSummaries))
78+
mux.HandleFunc("/goroutines", GoroutinesHandlerFunc(parsed.summary.Goroutines))
79+
mux.HandleFunc("/goroutine", GoroutineHandler(parsed.summary.Goroutines))
8080

8181
// MMU handler.
8282
mux.HandleFunc("/mmu", traceviewer.MMUHandlerFunc(ranges, mutatorUtil))
@@ -98,16 +98,16 @@ func Main(traceFile, httpAddr, pprof string, debug int) error {
9898
}
9999

100100
type parsedTrace struct {
101-
events []tracev2.Event
102-
gSummaries map[tracev2.GoID]*trace.GoroutineSummary
101+
events []tracev2.Event
102+
summary *trace.Summary
103103
}
104104

105105
func parseTrace(tr io.Reader) (*parsedTrace, error) {
106106
r, err := tracev2.NewReader(tr)
107107
if err != nil {
108108
return nil, fmt.Errorf("failed to create trace reader: %w", err)
109109
}
110-
s := trace.NewGoroutineSummarizer()
110+
s := trace.NewSummarizer()
111111
t := new(parsedTrace)
112112
for {
113113
ev, err := r.ReadEvent()
@@ -119,7 +119,7 @@ func parseTrace(tr io.Reader) (*parsedTrace, error) {
119119
t.events = append(t.events, ev)
120120
s.Event(&t.events[len(t.events)-1])
121121
}
122-
t.gSummaries = s.Finalize()
122+
t.summary = s.Finalize()
123123
return t, nil
124124
}
125125

src/cmd/trace/v2/pprof.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func pprofMatchingGoroutines(id string, t *parsedTrace) (map[tracev2.GoID][]inte
5757
return nil, fmt.Errorf("invalid goroutine type: %v", id)
5858
}
5959
res := make(map[tracev2.GoID][]interval)
60-
for _, g := range t.gSummaries {
60+
for _, g := range t.summary.Goroutines {
6161
if g.PC != pc {
6262
continue
6363
}
@@ -81,7 +81,7 @@ func pprofMatchingRegions(filter *regionFilter, t *parsedTrace) (map[tracev2.GoI
8181
}
8282

8383
gToIntervals := make(map[tracev2.GoID][]interval)
84-
for _, g := range t.gSummaries {
84+
for _, g := range t.summary.Goroutines {
8585
for _, r := range g.Regions {
8686
if !filter.match(t, r) {
8787
continue

src/internal/trace/goroutinesv2.go renamed to src/internal/trace/summary.go

+93-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import (
1111
"time"
1212
)
1313

14+
// Summary is the analysis result produced by the summarizer.
15+
type Summary struct {
16+
Goroutines map[tracev2.GoID]*GoroutineSummary
17+
Tasks map[tracev2.TaskID]*UserTaskSummary
18+
}
19+
1420
// GoroutineSummary contains statistics and execution details of a single goroutine.
1521
// (For v2 traces.)
1622
type GoroutineSummary struct {
@@ -35,6 +41,29 @@ type GoroutineSummary struct {
3541
*goroutineSummary
3642
}
3743

44+
// UserTaskSummary represents a task in the trace.
45+
type UserTaskSummary struct {
46+
ID tracev2.TaskID
47+
Name string
48+
Parent *UserTaskSummary // nil if the parent is unknown.
49+
Children []*UserTaskSummary
50+
51+
// Task begin event. An EventTaskBegin event or nil.
52+
Start *tracev2.Event
53+
54+
// End end event. Normally EventTaskEnd event or nil,
55+
End *tracev2.Event
56+
57+
// Logs is a list of tracev2.EventLog events associated with the task.
58+
Logs []*tracev2.Event
59+
60+
// List of regions in the task, sorted based on the start time.
61+
Regions []*UserRegionSummary
62+
63+
// Goroutines is the set of goroutines associated with this task.
64+
Goroutines map[tracev2.GoID]*GoroutineSummary
65+
}
66+
3867
// UserRegionSummary represents a region and goroutine execution stats
3968
// while the region was active. (For v2 traces.)
4069
type UserRegionSummary struct {
@@ -209,11 +238,14 @@ type goroutineSummary struct {
209238
activeRegions []*UserRegionSummary // stack of active regions
210239
}
211240

212-
// GoroutineSummarizer constructs per-goroutine time statistics for v2 traces.
213-
type GoroutineSummarizer struct {
241+
// Summarizer constructs per-goroutine time statistics for v2 traces.
242+
type Summarizer struct {
214243
// gs contains the map of goroutine summaries we're building up to return to the caller.
215244
gs map[tracev2.GoID]*GoroutineSummary
216245

246+
// tasks contains the map of task summaries we're building up to return to the caller.
247+
tasks map[tracev2.TaskID]*UserTaskSummary
248+
217249
// syscallingP and syscallingG represent a binding between a P and G in a syscall.
218250
// Used to correctly identify and clean up after syscalls (blocking or otherwise).
219251
syscallingP map[tracev2.ProcID]tracev2.GoID
@@ -229,10 +261,11 @@ type GoroutineSummarizer struct {
229261
syncTs tracev2.Time // timestamp of the last sync event processed (or the first timestamp in the trace).
230262
}
231263

232-
// NewGoroutineSummarizer creates a new struct to build goroutine stats from a trace.
233-
func NewGoroutineSummarizer() *GoroutineSummarizer {
234-
return &GoroutineSummarizer{
264+
// NewSummarizer creates a new struct to build goroutine stats from a trace.
265+
func NewSummarizer() *Summarizer {
266+
return &Summarizer{
235267
gs: make(map[tracev2.GoID]*GoroutineSummary),
268+
tasks: make(map[tracev2.TaskID]*UserTaskSummary),
236269
syscallingP: make(map[tracev2.ProcID]tracev2.GoID),
237270
syscallingG: make(map[tracev2.GoID]tracev2.ProcID),
238271
rangesP: make(map[rangeP]tracev2.GoID),
@@ -245,7 +278,7 @@ type rangeP struct {
245278
}
246279

247280
// Event feeds a single event into the stats summarizer.
248-
func (s *GoroutineSummarizer) Event(ev *tracev2.Event) {
281+
func (s *Summarizer) Event(ev *tracev2.Event) {
249282
if s.syncTs == 0 {
250283
s.syncTs = ev.Time()
251284
}
@@ -460,12 +493,17 @@ func (s *GoroutineSummarizer) Event(ev *tracev2.Event) {
460493
case tracev2.EventRegionBegin:
461494
g := s.gs[ev.Goroutine()]
462495
r := ev.Region()
463-
g.activeRegions = append(g.activeRegions, &UserRegionSummary{
496+
region := &UserRegionSummary{
464497
Name: r.Type,
465498
TaskID: r.Task,
466499
Start: ev,
467500
GoroutineExecStats: g.snapshotStat(ev.Time()),
468-
})
501+
}
502+
g.activeRegions = append(g.activeRegions, region)
503+
// Associate the region and current goroutine to the task.
504+
task := s.getOrAddTask(r.Task)
505+
task.Regions = append(task.Regions, region)
506+
task.Goroutines[g.ID] = g
469507
case tracev2.EventRegionEnd:
470508
g := s.gs[ev.Goroutine()]
471509
r := ev.Region()
@@ -476,19 +514,61 @@ func (s *GoroutineSummarizer) Event(ev *tracev2.Event) {
476514
sd = regionStk[n-1]
477515
regionStk = regionStk[:n-1]
478516
g.activeRegions = regionStk
517+
// N.B. No need to add the region to a task; the EventRegionBegin already handled it.
479518
} else {
480519
// This is an "end" without a start. Just fabricate the region now.
481520
sd = &UserRegionSummary{Name: r.Type, TaskID: r.Task}
521+
// Associate the region and current goroutine to the task.
522+
task := s.getOrAddTask(r.Task)
523+
task.Goroutines[g.ID] = g
524+
task.Regions = append(task.Regions, sd)
482525
}
483526
sd.GoroutineExecStats = g.snapshotStat(ev.Time()).sub(sd.GoroutineExecStats)
484527
sd.End = ev
485528
g.Regions = append(g.Regions, sd)
529+
530+
// Handle tasks and logs.
531+
case tracev2.EventTaskBegin, tracev2.EventTaskEnd:
532+
// Initialize the task.
533+
t := ev.Task()
534+
task := s.getOrAddTask(t.ID)
535+
task.Name = t.Type
536+
task.Goroutines[ev.Goroutine()] = s.gs[ev.Goroutine()]
537+
if ev.Kind() == tracev2.EventTaskBegin {
538+
task.Start = ev
539+
} else {
540+
task.End = ev
541+
}
542+
// Initialize the parent, if one exists and it hasn't been done yet.
543+
// We need to avoid doing it twice, otherwise we could appear twice
544+
// in the parent's Children list.
545+
if t.Parent != tracev2.NoTask && task.Parent == nil {
546+
parent := s.getOrAddTask(t.Parent)
547+
task.Parent = parent
548+
parent.Children = append(parent.Children, task)
549+
}
550+
case tracev2.EventLog:
551+
log := ev.Log()
552+
// Just add the log to the task. We'll create the task if it
553+
// doesn't exist (it's just been mentioned now).
554+
task := s.getOrAddTask(log.Task)
555+
task.Goroutines[ev.Goroutine()] = s.gs[ev.Goroutine()]
556+
task.Logs = append(task.Logs, ev)
486557
}
487558
}
488559

560+
func (s *Summarizer) getOrAddTask(id tracev2.TaskID) *UserTaskSummary {
561+
task := s.tasks[id]
562+
if task == nil {
563+
task = &UserTaskSummary{ID: id, Goroutines: make(map[tracev2.GoID]*GoroutineSummary)}
564+
s.tasks[id] = task
565+
}
566+
return task
567+
}
568+
489569
// Finalize indicates to the summarizer that we're done processing the trace.
490570
// It cleans up any remaining state and returns the full summary.
491-
func (s *GoroutineSummarizer) Finalize() map[tracev2.GoID]*GoroutineSummary {
571+
func (s *Summarizer) Finalize() *Summary {
492572
for _, g := range s.gs {
493573
g.finalize(s.lastTs, nil)
494574

@@ -506,7 +586,10 @@ func (s *GoroutineSummarizer) Finalize() map[tracev2.GoID]*GoroutineSummary {
506586
})
507587
g.goroutineSummary = nil
508588
}
509-
return s.gs
589+
return &Summary{
590+
Goroutines: s.gs,
591+
Tasks: s.tasks,
592+
}
510593
}
511594

512595
// RelatedGoroutinesV2 finds a set of goroutines related to goroutine goid for v2 traces.

0 commit comments

Comments
 (0)