}
for i, task := range opts.tasks {
emitTask(ctx, task, i)
+ if opts.mode&traceviewer.ModeGoroutineOriented != 0 {
+ for _, region := range task.Regions {
+ emitRegion(ctx, region)
+ }
+ }
}
g.Finish(ctx)
}
}
}
+// emitRegion emits goroutine-based slice events to the UI. The caller
+// must be emitting for a goroutine-oriented trace.
+//
+// TODO(mknyszek): Make regions part of the regular generator loop and
+// treat them like ranges so that we can emit regions in traces oriented
+// by proc or thread.
+func emitRegion(ctx *traceContext, region *trace.UserRegionSummary) {
+ if region.Name == "" {
+ return
+ }
+ // Collect information about the region.
+ var startStack, endStack tracev2.Stack
+ goroutine := tracev2.NoGoroutine
+ startTime, endTime := ctx.startTime, ctx.endTime
+ if region.Start != nil {
+ startStack = region.Start.Stack()
+ startTime = region.Start.Time()
+ goroutine = region.Start.Goroutine()
+ }
+ if region.End != nil {
+ endStack = region.End.Stack()
+ endTime = region.End.Time()
+ goroutine = region.End.Goroutine()
+ }
+ if goroutine == tracev2.NoGoroutine {
+ return
+ }
+ arg := struct {
+ TaskID uint64 `json:"taskid"`
+ }{
+ TaskID: uint64(region.TaskID),
+ }
+ ctx.AsyncSlice(traceviewer.AsyncSliceEvent{
+ SliceEvent: traceviewer.SliceEvent{
+ Name: region.Name,
+ Ts: ctx.elapsed(startTime),
+ Dur: endTime.Sub(startTime),
+ Resource: uint64(goroutine),
+ Stack: ctx.Stack(viewerFrames(startStack)),
+ EndStack: ctx.Stack(viewerFrames(endStack)),
+ Arg: arg,
+ },
+ Category: "Region",
+ Scope: fmt.Sprintf("%x", region.TaskID),
+ TaskColorIndex: uint64(region.TaskID),
+ })
+}
+
// Building blocks for generators.
// stackSampleGenerator implements a generic handler for stack sample events.
resources map[uint64]string
focusResource uint64
tasks map[uint64]task
+ asyncSliceSeq uint64
}
type task struct {
Arg: s.Arg,
Cname: cname,
})
-
}
type SliceEvent struct {
Arg any
}
+func (e *Emitter) AsyncSlice(s AsyncSliceEvent) {
+ if !e.tsWithinRange(s.Ts) && !e.tsWithinRange(s.Ts+s.Dur) {
+ return
+ }
+ if e.filter != nil && !e.filter(s.Resource) {
+ return
+ }
+ cname := ""
+ if s.TaskColorIndex != 0 {
+ cname = pickTaskColor(s.TaskColorIndex)
+ }
+ e.asyncSliceSeq++
+ e.OptionalEvent(&format.Event{
+ Category: s.Category,
+ Name: s.Name,
+ Phase: "b",
+ Time: viewerTime(s.Ts),
+ TID: s.Resource,
+ ID: e.asyncSliceSeq,
+ Scope: s.Scope,
+ Stack: s.Stack,
+ Cname: cname,
+ })
+ e.OptionalEvent(&format.Event{
+ Category: s.Category,
+ Name: s.Name,
+ Phase: "e",
+ Time: viewerTime(s.Ts + s.Dur),
+ TID: s.Resource,
+ ID: e.asyncSliceSeq,
+ Scope: s.Scope,
+ Stack: s.EndStack,
+ Arg: s.Arg,
+ Cname: cname,
+ })
+}
+
+type AsyncSliceEvent struct {
+ SliceEvent
+ Category string
+ Scope string
+ TaskColorIndex uint64 // Take on the same color as the task with this ID.
+}
+
func (e *Emitter) Instant(i InstantEvent) {
if !e.tsWithinRange(i.Ts) {
return