@@ -11,6 +11,12 @@ import (
11
11
"time"
12
12
)
13
13
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
+
14
20
// GoroutineSummary contains statistics and execution details of a single goroutine.
15
21
// (For v2 traces.)
16
22
type GoroutineSummary struct {
@@ -35,6 +41,29 @@ type GoroutineSummary struct {
35
41
* goroutineSummary
36
42
}
37
43
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
+
38
67
// UserRegionSummary represents a region and goroutine execution stats
39
68
// while the region was active. (For v2 traces.)
40
69
type UserRegionSummary struct {
@@ -209,11 +238,14 @@ type goroutineSummary struct {
209
238
activeRegions []* UserRegionSummary // stack of active regions
210
239
}
211
240
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 {
214
243
// gs contains the map of goroutine summaries we're building up to return to the caller.
215
244
gs map [tracev2.GoID ]* GoroutineSummary
216
245
246
+ // tasks contains the map of task summaries we're building up to return to the caller.
247
+ tasks map [tracev2.TaskID ]* UserTaskSummary
248
+
217
249
// syscallingP and syscallingG represent a binding between a P and G in a syscall.
218
250
// Used to correctly identify and clean up after syscalls (blocking or otherwise).
219
251
syscallingP map [tracev2.ProcID ]tracev2.GoID
@@ -229,10 +261,11 @@ type GoroutineSummarizer struct {
229
261
syncTs tracev2.Time // timestamp of the last sync event processed (or the first timestamp in the trace).
230
262
}
231
263
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 {
235
267
gs : make (map [tracev2.GoID ]* GoroutineSummary ),
268
+ tasks : make (map [tracev2.TaskID ]* UserTaskSummary ),
236
269
syscallingP : make (map [tracev2.ProcID ]tracev2.GoID ),
237
270
syscallingG : make (map [tracev2.GoID ]tracev2.ProcID ),
238
271
rangesP : make (map [rangeP ]tracev2.GoID ),
@@ -245,7 +278,7 @@ type rangeP struct {
245
278
}
246
279
247
280
// 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 ) {
249
282
if s .syncTs == 0 {
250
283
s .syncTs = ev .Time ()
251
284
}
@@ -460,12 +493,17 @@ func (s *GoroutineSummarizer) Event(ev *tracev2.Event) {
460
493
case tracev2 .EventRegionBegin :
461
494
g := s .gs [ev .Goroutine ()]
462
495
r := ev .Region ()
463
- g . activeRegions = append ( g . activeRegions , & UserRegionSummary {
496
+ region := & UserRegionSummary {
464
497
Name : r .Type ,
465
498
TaskID : r .Task ,
466
499
Start : ev ,
467
500
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
469
507
case tracev2 .EventRegionEnd :
470
508
g := s .gs [ev .Goroutine ()]
471
509
r := ev .Region ()
@@ -476,19 +514,61 @@ func (s *GoroutineSummarizer) Event(ev *tracev2.Event) {
476
514
sd = regionStk [n - 1 ]
477
515
regionStk = regionStk [:n - 1 ]
478
516
g .activeRegions = regionStk
517
+ // N.B. No need to add the region to a task; the EventRegionBegin already handled it.
479
518
} else {
480
519
// This is an "end" without a start. Just fabricate the region now.
481
520
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 )
482
525
}
483
526
sd .GoroutineExecStats = g .snapshotStat (ev .Time ()).sub (sd .GoroutineExecStats )
484
527
sd .End = ev
485
528
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 )
486
557
}
487
558
}
488
559
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
+
489
569
// Finalize indicates to the summarizer that we're done processing the trace.
490
570
// 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 {
492
572
for _ , g := range s .gs {
493
573
g .finalize (s .lastTs , nil )
494
574
@@ -506,7 +586,10 @@ func (s *GoroutineSummarizer) Finalize() map[tracev2.GoID]*GoroutineSummary {
506
586
})
507
587
g .goroutineSummary = nil
508
588
}
509
- return s .gs
589
+ return & Summary {
590
+ Goroutines : s .gs ,
591
+ Tasks : s .tasks ,
592
+ }
510
593
}
511
594
512
595
// RelatedGoroutinesV2 finds a set of goroutines related to goroutine goid for v2 traces.
0 commit comments