]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/trace/v2: add support for the goroutine-oriented task view
authorMichael Anthony Knyszek <mknyszek@google.com>
Mon, 20 Nov 2023 19:29:42 +0000 (19:29 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 21 Nov 2023 21:29:55 +0000 (21:29 +0000)
This change adds support for a goroutine-oriented task view via the
/trace?taskid=<taskid> endpoint. This works but it's missing regions.
That will be implemented in a follow-up CL.

For #60773.
For #63960.

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

index 85ee52e47e05742d6eca019d4f1c2cd194fb45be..e4ca613678adff809e8eddffcfc7908e74db1c78 100644 (file)
@@ -64,39 +64,54 @@ func JSONTraceHandler(parsed *parsedTrace) http.Handler {
                                log.Printf("failed to find task with id %d", taskid)
                                return
                        }
-                       opts.mode = traceviewer.ModeTaskOriented
-                       if task.Start != nil {
-                               opts.startTime = task.Start.Time().Sub(parsed.startTime())
-                       } else { // The task started before the trace did.
-                               opts.startTime = 0
+                       opts.setTask(parsed, task)
+               } else if taskids := r.FormValue("taskid"); taskids != "" {
+                       taskid, err := strconv.ParseUint(taskids, 10, 64)
+                       if err != nil {
+                               log.Printf("failed to parse taskid parameter %q: %v", taskids, err)
+                               return
                        }
-                       if task.End != nil {
-                               opts.endTime = task.End.Time().Sub(parsed.startTime())
-                       } else { // The task didn't end.
-                               opts.endTime = parsed.endTime().Sub(parsed.startTime())
+                       task, ok := parsed.summary.Tasks[tracev2.TaskID(taskid)]
+                       if !ok {
+                               log.Printf("failed to find task with id %d", taskid)
+                               return
                        }
-                       opts.tasks = task.Descendents()
-                       slices.SortStableFunc(opts.tasks, func(a, b *trace.UserTaskSummary) int {
-                               aStart, bStart := parsed.startTime(), parsed.startTime()
-                               if a.Start != nil {
-                                       aStart = a.Start.Time()
-                               }
-                               if b.Start != nil {
-                                       bStart = b.Start.Time()
-                               }
-                               if a.Start != b.Start {
-                                       return cmp.Compare(aStart, bStart)
+                       // This mode is goroutine-oriented.
+                       opts.mode = traceviewer.ModeGoroutineOriented
+                       opts.setTask(parsed, task)
+
+                       // Pick the goroutine to orient ourselves around by just
+                       // trying to pick the earliest event in the task that makes
+                       // any sense. Though, we always want the start if that's there.
+                       var firstEv *tracev2.Event
+                       if task.Start != nil {
+                               firstEv = task.Start
+                       } else {
+                               for _, logEv := range task.Logs {
+                                       if firstEv == nil || logEv.Time() < firstEv.Time() {
+                                               firstEv = logEv
+                                       }
                                }
-                               // Break ties with the end time.
-                               aEnd, bEnd := parsed.endTime(), parsed.endTime()
-                               if a.End != nil {
-                                       aEnd = a.End.Time()
+                               if task.End != nil && (firstEv == nil || task.End.Time() < firstEv.Time()) {
+                                       firstEv = task.End
                                }
-                               if b.End != nil {
-                                       bEnd = b.End.Time()
+                       }
+                       if firstEv == nil || firstEv.Goroutine() == tracev2.NoGoroutine {
+                               log.Printf("failed to find task with id %d", taskid)
+                               return
+                       }
+
+                       // Set the goroutine filtering options.
+                       goid := firstEv.Goroutine()
+                       opts.focusGoroutine = goid
+                       goroutines := make(map[tracev2.GoID]struct{})
+                       for _, task := range opts.tasks {
+                               // Find only directly involved goroutines.
+                               for id := range task.Goroutines {
+                                       goroutines[id] = struct{}{}
                                }
-                               return cmp.Compare(aEnd, bEnd)
-                       })
+                       }
+                       opts.goroutines = goroutines
                }
 
                // Parse start and end options. Both or none must be present.
@@ -149,6 +164,43 @@ type genOpts struct {
        tasks          []*trace.UserTaskSummary
 }
 
+// setTask sets a task to focus on.
+func (opts *genOpts) setTask(parsed *parsedTrace, task *trace.UserTaskSummary) {
+       opts.mode |= traceviewer.ModeTaskOriented
+       if task.Start != nil {
+               opts.startTime = task.Start.Time().Sub(parsed.startTime())
+       } else { // The task started before the trace did.
+               opts.startTime = 0
+       }
+       if task.End != nil {
+               opts.endTime = task.End.Time().Sub(parsed.startTime())
+       } else { // The task didn't end.
+               opts.endTime = parsed.endTime().Sub(parsed.startTime())
+       }
+       opts.tasks = task.Descendents()
+       slices.SortStableFunc(opts.tasks, func(a, b *trace.UserTaskSummary) int {
+               aStart, bStart := parsed.startTime(), parsed.startTime()
+               if a.Start != nil {
+                       aStart = a.Start.Time()
+               }
+               if b.Start != nil {
+                       bStart = b.Start.Time()
+               }
+               if a.Start != b.Start {
+                       return cmp.Compare(aStart, bStart)
+               }
+               // Break ties with the end time.
+               aEnd, bEnd := parsed.endTime(), parsed.endTime()
+               if a.End != nil {
+                       aEnd = a.End.Time()
+               }
+               if b.End != nil {
+                       bEnd = b.End.Time()
+               }
+               return cmp.Compare(aEnd, bEnd)
+       })
+}
+
 func defaultGenOpts() *genOpts {
        return &genOpts{
                startTime: time.Duration(0),