]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/trace/v2: emit regions in the goroutine-oriented task view
authorMichael Anthony Knyszek <mknyszek@google.com>
Mon, 20 Nov 2023 21:19:35 +0000 (21:19 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 21 Nov 2023 21:29:58 +0000 (21:29 +0000)
This change emits regions in the goroutine-oriented task view (the
/trace endpoint with the taskid query variable set) in the same way the
old cmd/trace does.

For #60773.
Fixes #63960.

Change-Id: If6c3e7072c694c84a7d2d6c34df668f48d3acc2a
Reviewed-on: https://go-review.googlesource.com/c/go/+/543995
Reviewed-by: Michael Pratt <mpratt@google.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/trace/v2/gen.go
src/internal/trace/traceviewer/emitter.go

index ac20dd113112bec50628a4b8f2384d6e8569cf6f..ad1599db92ddae098db7577822ca3bc298be2aa2 100644 (file)
@@ -73,6 +73,11 @@ func runGenerator(ctx *traceContext, g generator, parsed *parsedTrace, opts *gen
        }
        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)
 }
@@ -130,6 +135,54 @@ func emitTask(ctx *traceContext, task *trace.UserTaskSummary, sortIndex int) {
        }
 }
 
+// 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.
index ad3112d8b976e2184ff47ac08ad58187a8c3a559..c91c743a7bfb6c83141a576fe74724a61ef51b1c 100644 (file)
@@ -317,6 +317,7 @@ type Emitter struct {
        resources                    map[uint64]string
        focusResource                uint64
        tasks                        map[uint64]task
+       asyncSliceSeq                uint64
 }
 
 type task struct {
@@ -376,7 +377,6 @@ func (e *Emitter) slice(s SliceEvent, sectionID uint64, cname string) {
                Arg:      s.Arg,
                Cname:    cname,
        })
-
 }
 
 type SliceEvent struct {
@@ -389,6 +389,50 @@ 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