1
2
3
4
5 package main
6
7 import (
8 "fmt"
9 "internal/trace"
10 "internal/trace/traceviewer"
11 "strings"
12 )
13
14
15
16
17 type generator interface {
18
19 Sync()
20 StackSample(ctx *traceContext, ev *trace.Event)
21 GlobalRange(ctx *traceContext, ev *trace.Event)
22 GlobalMetric(ctx *traceContext, ev *trace.Event)
23
24
25 GoroutineLabel(ctx *traceContext, ev *trace.Event)
26 GoroutineRange(ctx *traceContext, ev *trace.Event)
27 GoroutineTransition(ctx *traceContext, ev *trace.Event)
28
29
30 ProcRange(ctx *traceContext, ev *trace.Event)
31 ProcTransition(ctx *traceContext, ev *trace.Event)
32
33
34 Log(ctx *traceContext, ev *trace.Event)
35
36
37 Finish(ctx *traceContext)
38 }
39
40
41 func runGenerator(ctx *traceContext, g generator, parsed *parsedTrace, opts *genOpts) {
42 for i := range parsed.events {
43 ev := &parsed.events[i]
44
45 switch ev.Kind() {
46 case trace.EventSync:
47 g.Sync()
48 case trace.EventStackSample:
49 g.StackSample(ctx, ev)
50 case trace.EventRangeBegin, trace.EventRangeActive, trace.EventRangeEnd:
51 r := ev.Range()
52 switch r.Scope.Kind {
53 case trace.ResourceGoroutine:
54 g.GoroutineRange(ctx, ev)
55 case trace.ResourceProc:
56 g.ProcRange(ctx, ev)
57 case trace.ResourceNone:
58 g.GlobalRange(ctx, ev)
59 }
60 case trace.EventMetric:
61 g.GlobalMetric(ctx, ev)
62 case trace.EventLabel:
63 l := ev.Label()
64 if l.Resource.Kind == trace.ResourceGoroutine {
65 g.GoroutineLabel(ctx, ev)
66 }
67 case trace.EventStateTransition:
68 switch ev.StateTransition().Resource.Kind {
69 case trace.ResourceProc:
70 g.ProcTransition(ctx, ev)
71 case trace.ResourceGoroutine:
72 g.GoroutineTransition(ctx, ev)
73 }
74 case trace.EventLog:
75 g.Log(ctx, ev)
76 }
77 }
78 for i, task := range opts.tasks {
79 emitTask(ctx, task, i)
80 if opts.mode&traceviewer.ModeGoroutineOriented != 0 {
81 for _, region := range task.Regions {
82 emitRegion(ctx, region)
83 }
84 }
85 }
86 g.Finish(ctx)
87 }
88
89
90
91
92
93 func emitTask(ctx *traceContext, task *trace.UserTaskSummary, sortIndex int) {
94
95 var startStack, endStack trace.Stack
96 var startG, endG trace.GoID
97 startTime, endTime := ctx.startTime, ctx.endTime
98 if task.Start != nil {
99 startStack = task.Start.Stack()
100 startG = task.Start.Goroutine()
101 startTime = task.Start.Time()
102 }
103 if task.End != nil {
104 endStack = task.End.Stack()
105 endG = task.End.Goroutine()
106 endTime = task.End.Time()
107 }
108 arg := struct {
109 ID uint64 `json:"id"`
110 StartG uint64 `json:"start_g,omitempty"`
111 EndG uint64 `json:"end_g,omitempty"`
112 }{
113 ID: uint64(task.ID),
114 StartG: uint64(startG),
115 EndG: uint64(endG),
116 }
117
118
119 ctx.Task(uint64(task.ID), fmt.Sprintf("T%d %s", task.ID, task.Name), sortIndex)
120 ctx.TaskSlice(traceviewer.SliceEvent{
121 Name: task.Name,
122 Ts: ctx.elapsed(startTime),
123 Dur: endTime.Sub(startTime),
124 Resource: uint64(task.ID),
125 Stack: ctx.Stack(viewerFrames(startStack)),
126 EndStack: ctx.Stack(viewerFrames(endStack)),
127 Arg: arg,
128 })
129
130 if task.Parent != nil && task.Start != nil && task.Start.Kind() == trace.EventTaskBegin {
131 ctx.TaskArrow(traceviewer.ArrowEvent{
132 Name: "newTask",
133 Start: ctx.elapsed(task.Start.Time()),
134 End: ctx.elapsed(task.Start.Time()),
135 FromResource: uint64(task.Parent.ID),
136 ToResource: uint64(task.ID),
137 FromStack: ctx.Stack(viewerFrames(task.Start.Stack())),
138 })
139 }
140 }
141
142
143
144
145
146
147
148 func emitRegion(ctx *traceContext, region *trace.UserRegionSummary) {
149 if region.Name == "" {
150 return
151 }
152
153 var startStack, endStack trace.Stack
154 goroutine := trace.NoGoroutine
155 startTime, endTime := ctx.startTime, ctx.endTime
156 if region.Start != nil {
157 startStack = region.Start.Stack()
158 startTime = region.Start.Time()
159 goroutine = region.Start.Goroutine()
160 }
161 if region.End != nil {
162 endStack = region.End.Stack()
163 endTime = region.End.Time()
164 goroutine = region.End.Goroutine()
165 }
166 if goroutine == trace.NoGoroutine {
167 return
168 }
169 arg := struct {
170 TaskID uint64 `json:"taskid"`
171 }{
172 TaskID: uint64(region.TaskID),
173 }
174 ctx.AsyncSlice(traceviewer.AsyncSliceEvent{
175 SliceEvent: traceviewer.SliceEvent{
176 Name: region.Name,
177 Ts: ctx.elapsed(startTime),
178 Dur: endTime.Sub(startTime),
179 Resource: uint64(goroutine),
180 Stack: ctx.Stack(viewerFrames(startStack)),
181 EndStack: ctx.Stack(viewerFrames(endStack)),
182 Arg: arg,
183 },
184 Category: "Region",
185 Scope: fmt.Sprintf("%x", region.TaskID),
186 TaskColorIndex: uint64(region.TaskID),
187 })
188 }
189
190
191
192
193
194 type stackSampleGenerator[R resource] struct {
195
196 getResource func(*trace.Event) R
197 }
198
199
200 func (g *stackSampleGenerator[R]) StackSample(ctx *traceContext, ev *trace.Event) {
201 id := g.getResource(ev)
202 if id == R(noResource) {
203
204 return
205 }
206 ctx.Instant(traceviewer.InstantEvent{
207 Name: "CPU profile sample",
208 Ts: ctx.elapsed(ev.Time()),
209 Resource: uint64(id),
210 Stack: ctx.Stack(viewerFrames(ev.Stack())),
211 })
212 }
213
214
215
216 type globalRangeGenerator struct {
217 ranges map[string]activeRange
218 seenSync bool
219 }
220
221
222 func (g *globalRangeGenerator) Sync() {
223 g.seenSync = true
224 }
225
226
227
228 func (g *globalRangeGenerator) GlobalRange(ctx *traceContext, ev *trace.Event) {
229 if g.ranges == nil {
230 g.ranges = make(map[string]activeRange)
231 }
232 r := ev.Range()
233 switch ev.Kind() {
234 case trace.EventRangeBegin:
235 g.ranges[r.Name] = activeRange{ev.Time(), ev.Stack()}
236 case trace.EventRangeActive:
237
238 if !g.seenSync {
239
240 g.ranges[r.Name] = activeRange{ctx.startTime, ev.Stack()}
241 }
242 case trace.EventRangeEnd:
243
244
245 ar := g.ranges[r.Name]
246 if strings.Contains(r.Name, "GC") {
247 ctx.Slice(traceviewer.SliceEvent{
248 Name: r.Name,
249 Ts: ctx.elapsed(ar.time),
250 Dur: ev.Time().Sub(ar.time),
251 Resource: trace.GCP,
252 Stack: ctx.Stack(viewerFrames(ar.stack)),
253 EndStack: ctx.Stack(viewerFrames(ev.Stack())),
254 })
255 }
256 delete(g.ranges, r.Name)
257 }
258 }
259
260
261 func (g *globalRangeGenerator) Finish(ctx *traceContext) {
262 for name, ar := range g.ranges {
263 if !strings.Contains(name, "GC") {
264 continue
265 }
266 ctx.Slice(traceviewer.SliceEvent{
267 Name: name,
268 Ts: ctx.elapsed(ar.time),
269 Dur: ctx.endTime.Sub(ar.time),
270 Resource: trace.GCP,
271 Stack: ctx.Stack(viewerFrames(ar.stack)),
272 })
273 }
274 }
275
276
277 type globalMetricGenerator struct {
278 }
279
280
281 func (g *globalMetricGenerator) GlobalMetric(ctx *traceContext, ev *trace.Event) {
282 m := ev.Metric()
283 switch m.Name {
284 case "/memory/classes/heap/objects:bytes":
285 ctx.HeapAlloc(ctx.elapsed(ev.Time()), m.Value.Uint64())
286 case "/gc/heap/goal:bytes":
287 ctx.HeapGoal(ctx.elapsed(ev.Time()), m.Value.Uint64())
288 case "/sched/gomaxprocs:threads":
289 ctx.Gomaxprocs(m.Value.Uint64())
290 }
291 }
292
293
294
295 type procRangeGenerator struct {
296 ranges map[trace.Range]activeRange
297 seenSync bool
298 }
299
300
301 func (g *procRangeGenerator) Sync() {
302 g.seenSync = true
303 }
304
305
306
307 func (g *procRangeGenerator) ProcRange(ctx *traceContext, ev *trace.Event) {
308 if g.ranges == nil {
309 g.ranges = make(map[trace.Range]activeRange)
310 }
311 r := ev.Range()
312 switch ev.Kind() {
313 case trace.EventRangeBegin:
314 g.ranges[r] = activeRange{ev.Time(), ev.Stack()}
315 case trace.EventRangeActive:
316
317 if !g.seenSync {
318
319 g.ranges[r] = activeRange{ctx.startTime, ev.Stack()}
320 }
321 case trace.EventRangeEnd:
322
323 ar := g.ranges[r]
324 ctx.Slice(traceviewer.SliceEvent{
325 Name: r.Name,
326 Ts: ctx.elapsed(ar.time),
327 Dur: ev.Time().Sub(ar.time),
328 Resource: uint64(r.Scope.Proc()),
329 Stack: ctx.Stack(viewerFrames(ar.stack)),
330 EndStack: ctx.Stack(viewerFrames(ev.Stack())),
331 })
332 delete(g.ranges, r)
333 }
334 }
335
336
337 func (g *procRangeGenerator) Finish(ctx *traceContext) {
338 for r, ar := range g.ranges {
339 ctx.Slice(traceviewer.SliceEvent{
340 Name: r.Name,
341 Ts: ctx.elapsed(ar.time),
342 Dur: ctx.endTime.Sub(ar.time),
343 Resource: uint64(r.Scope.Proc()),
344 Stack: ctx.Stack(viewerFrames(ar.stack)),
345 })
346 }
347 }
348
349
350 type activeRange struct {
351 time trace.Time
352 stack trace.Stack
353 }
354
355
356 type completedRange struct {
357 name string
358 startTime trace.Time
359 endTime trace.Time
360 startStack trace.Stack
361 endStack trace.Stack
362 arg any
363 }
364
365 type logEventGenerator[R resource] struct {
366
367 getResource func(*trace.Event) R
368 }
369
370
371 func (g *logEventGenerator[R]) Log(ctx *traceContext, ev *trace.Event) {
372 id := g.getResource(ev)
373 if id == R(noResource) {
374
375 return
376 }
377
378
379 log := ev.Log()
380 name := log.Message
381 if log.Category != "" {
382 name = "[" + log.Category + "] " + name
383 }
384
385
386 ctx.Instant(traceviewer.InstantEvent{
387 Name: name,
388 Ts: ctx.elapsed(ev.Time()),
389 Category: "user event",
390 Resource: uint64(id),
391 Stack: ctx.Stack(viewerFrames(ev.Stack())),
392 })
393 }
394
View as plain text