[release-branch.go1.22] cmd/trace/v2: emit user log annotations in all views

This was an oversight in porting over cmd/trace to the new trace format
and API.

Fixes #65153.

Change-Id: I883d302f95956fcc9abb60aa53165acb6d099d67
Reviewed-on: https://go-review.googlesource.com/c/go/+/557175
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
(cherry picked from commit 7cb98c1da1d38447a272c50b2a33634ebb845aa4)
Reviewed-on: https://go-review.googlesource.com/c/go/+/557817
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
diff --git a/src/cmd/trace/v2/gen.go b/src/cmd/trace/v2/gen.go
index ad1599d..f6a4bb6 100644
--- a/src/cmd/trace/v2/gen.go
+++ b/src/cmd/trace/v2/gen.go
@@ -31,6 +31,9 @@
 	ProcRange(ctx *traceContext, ev *tracev2.Event)
 	ProcTransition(ctx *traceContext, ev *tracev2.Event)
 
+	// User annotations.
+	Log(ctx *traceContext, ev *tracev2.Event)
+
 	// Finish indicates the end of the trace and finalizes generation.
 	Finish(ctx *traceContext)
 }
@@ -69,6 +72,8 @@
 			case tracev2.ResourceGoroutine:
 				g.GoroutineTransition(ctx, ev)
 			}
+		case tracev2.EventLog:
+			g.Log(ctx, ev)
 		}
 	}
 	for i, task := range opts.tasks {
@@ -357,3 +362,33 @@
 	endStack   tracev2.Stack
 	arg        any
 }
+
+type logEventGenerator[R resource] struct {
+	// getResource is a function to extract a resource ID from a Log event.
+	getResource func(*tracev2.Event) R
+}
+
+// Log implements a log event handler. It expects ev to be one such event.
+func (g *logEventGenerator[R]) Log(ctx *traceContext, ev *tracev2.Event) {
+	id := g.getResource(ev)
+	if id == R(noResource) {
+		// We have nowhere to put this in the UI.
+		return
+	}
+
+	// Construct the name to present.
+	log := ev.Log()
+	name := log.Message
+	if log.Category != "" {
+		name = "[" + log.Category + "] " + name
+	}
+
+	// Emit an instant event.
+	ctx.Instant(traceviewer.InstantEvent{
+		Name:     name,
+		Ts:       ctx.elapsed(ev.Time()),
+		Category: "user event",
+		Resource: uint64(id),
+		Stack:    ctx.Stack(viewerFrames(ev.Stack())),
+	})
+}
diff --git a/src/cmd/trace/v2/goroutinegen.go b/src/cmd/trace/v2/goroutinegen.go
index eb1aea9..c76bd84 100644
--- a/src/cmd/trace/v2/goroutinegen.go
+++ b/src/cmd/trace/v2/goroutinegen.go
@@ -14,6 +14,7 @@
 	globalRangeGenerator
 	globalMetricGenerator
 	stackSampleGenerator[tracev2.GoID]
+	logEventGenerator[tracev2.GoID]
 
 	gStates map[tracev2.GoID]*gState[tracev2.GoID]
 	focus   tracev2.GoID
@@ -22,9 +23,11 @@
 
 func newGoroutineGenerator(ctx *traceContext, focus tracev2.GoID, filter map[tracev2.GoID]struct{}) *goroutineGenerator {
 	gg := new(goroutineGenerator)
-	gg.stackSampleGenerator.getResource = func(ev *tracev2.Event) tracev2.GoID {
+	rg := func(ev *tracev2.Event) tracev2.GoID {
 		return ev.Goroutine()
 	}
+	gg.stackSampleGenerator.getResource = rg
+	gg.logEventGenerator.getResource = rg
 	gg.gStates = make(map[tracev2.GoID]*gState[tracev2.GoID])
 	gg.focus = focus
 	gg.filter = filter
diff --git a/src/cmd/trace/v2/procgen.go b/src/cmd/trace/v2/procgen.go
index 30ed568..41e3795 100644
--- a/src/cmd/trace/v2/procgen.go
+++ b/src/cmd/trace/v2/procgen.go
@@ -18,6 +18,7 @@
 	globalMetricGenerator
 	procRangeGenerator
 	stackSampleGenerator[tracev2.ProcID]
+	logEventGenerator[tracev2.ProcID]
 
 	gStates   map[tracev2.GoID]*gState[tracev2.ProcID]
 	inSyscall map[tracev2.ProcID]*gState[tracev2.ProcID]
@@ -26,9 +27,11 @@
 
 func newProcGenerator() *procGenerator {
 	pg := new(procGenerator)
-	pg.stackSampleGenerator.getResource = func(ev *tracev2.Event) tracev2.ProcID {
+	rg := func(ev *tracev2.Event) tracev2.ProcID {
 		return ev.Proc()
 	}
+	pg.stackSampleGenerator.getResource = rg
+	pg.logEventGenerator.getResource = rg
 	pg.gStates = make(map[tracev2.GoID]*gState[tracev2.ProcID])
 	pg.inSyscall = make(map[tracev2.ProcID]*gState[tracev2.ProcID])
 	return pg
diff --git a/src/cmd/trace/v2/threadgen.go b/src/cmd/trace/v2/threadgen.go
index c2d2071..e1cae2b 100644
--- a/src/cmd/trace/v2/threadgen.go
+++ b/src/cmd/trace/v2/threadgen.go
@@ -17,6 +17,7 @@
 	globalRangeGenerator
 	globalMetricGenerator
 	stackSampleGenerator[tracev2.ThreadID]
+	logEventGenerator[tracev2.ThreadID]
 
 	gStates map[tracev2.GoID]*gState[tracev2.ThreadID]
 	threads map[tracev2.ThreadID]struct{}
@@ -24,9 +25,11 @@
 
 func newThreadGenerator() *threadGenerator {
 	tg := new(threadGenerator)
-	tg.stackSampleGenerator.getResource = func(ev *tracev2.Event) tracev2.ThreadID {
+	rg := func(ev *tracev2.Event) tracev2.ThreadID {
 		return ev.Thread()
 	}
+	tg.stackSampleGenerator.getResource = rg
+	tg.logEventGenerator.getResource = rg
 	tg.gStates = make(map[tracev2.GoID]*gState[tracev2.ThreadID])
 	tg.threads = make(map[tracev2.ThreadID]struct{})
 	return tg