1
2
3
4
5 package trace
6
7 import (
8 "fmt"
9 "iter"
10 "math"
11 "strings"
12 "time"
13
14 "internal/trace/event"
15 "internal/trace/event/go122"
16 "internal/trace/version"
17 )
18
19
20
21
22
23 type EventKind uint16
24
25 const (
26 EventBad EventKind = iota
27
28
29
30
31
32 EventSync
33
34
35
36 EventMetric
37
38
39 EventLabel
40
41
42
43
44
45
46
47
48
49 EventStackSample
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 EventRangeBegin
66 EventRangeActive
67 EventRangeEnd
68
69
70 EventTaskBegin
71 EventTaskEnd
72
73
74 EventRegionBegin
75 EventRegionEnd
76
77
78 EventLog
79
80
81 EventStateTransition
82
83
84
85
86 EventExperimental
87 )
88
89
90 func (e EventKind) String() string {
91 if int(e) >= len(eventKindStrings) {
92 return eventKindStrings[0]
93 }
94 return eventKindStrings[e]
95 }
96
97 var eventKindStrings = [...]string{
98 EventBad: "Bad",
99 EventSync: "Sync",
100 EventMetric: "Metric",
101 EventLabel: "Label",
102 EventStackSample: "StackSample",
103 EventRangeBegin: "RangeBegin",
104 EventRangeActive: "RangeActive",
105 EventRangeEnd: "RangeEnd",
106 EventTaskBegin: "TaskBegin",
107 EventTaskEnd: "TaskEnd",
108 EventRegionBegin: "RegionBegin",
109 EventRegionEnd: "RegionEnd",
110 EventLog: "Log",
111 EventStateTransition: "StateTransition",
112 EventExperimental: "Experimental",
113 }
114
115 const maxTime = Time(math.MaxInt64)
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 type Time int64
137
138
139 func (t Time) Sub(t0 Time) time.Duration {
140 return time.Duration(int64(t) - int64(t0))
141 }
142
143
144 type Metric struct {
145
146
147
148
149
150
151
152
153 Name string
154
155
156
157
158
159 Value Value
160 }
161
162
163 type Label struct {
164
165 Label string
166
167
168 Resource ResourceID
169 }
170
171
172 type Range struct {
173
174
175
176
177
178
179
180 Name string
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 Scope ResourceID
198 }
199
200
201 type RangeAttribute struct {
202
203 Name string
204
205
206 Value Value
207 }
208
209
210
211 type TaskID uint64
212
213 const (
214
215 NoTask = TaskID(^uint64(0))
216
217
218
219 BackgroundTask = TaskID(0)
220 )
221
222
223 type Task struct {
224
225
226
227 ID TaskID
228
229
230 Parent TaskID
231
232
233
234
235 Type string
236 }
237
238
239 type Region struct {
240
241 Task TaskID
242
243
244 Type string
245 }
246
247
248 type Log struct {
249
250 Task TaskID
251
252
253 Category string
254
255
256 Message string
257 }
258
259
260
261
262
263 type Stack struct {
264 table *evTable
265 id stackID
266 }
267
268
269 func (s Stack) Frames() iter.Seq[StackFrame] {
270 return func(yield func(StackFrame) bool) {
271 if s.id == 0 {
272 return
273 }
274 stk := s.table.stacks.mustGet(s.id)
275 for _, pc := range stk.pcs {
276 f := s.table.pcs[pc]
277 sf := StackFrame{
278 PC: f.pc,
279 Func: s.table.strings.mustGet(f.funcID),
280 File: s.table.strings.mustGet(f.fileID),
281 Line: f.line,
282 }
283 if !yield(sf) {
284 return
285 }
286 }
287 }
288 }
289
290
291
292 var NoStack = Stack{}
293
294
295 type StackFrame struct {
296
297
298
299 PC uint64
300
301
302 Func string
303
304
305 File string
306
307
308 Line uint64
309 }
310
311
312 type ExperimentalEvent struct {
313
314 Name string
315
316
317
318 ArgNames []string
319
320
321 Args []uint64
322
323
324
325
326 Data *ExperimentalData
327 }
328
329
330
331
332
333 type ExperimentalData struct {
334
335 Batches []ExperimentalBatch
336 }
337
338
339 type ExperimentalBatch struct {
340
341 Thread ThreadID
342
343
344 Data []byte
345 }
346
347
348 type Event struct {
349 table *evTable
350 ctx schedCtx
351 base baseEvent
352 }
353
354
355 func (e Event) Kind() EventKind {
356 return go122Type2Kind[e.base.typ]
357 }
358
359
360 func (e Event) Time() Time {
361 return e.base.time
362 }
363
364
365
366
367
368
369
370
371
372
373 func (e Event) Goroutine() GoID {
374 return e.ctx.G
375 }
376
377
378
379
380
381
382 func (e Event) Proc() ProcID {
383 return e.ctx.P
384 }
385
386
387
388
389
390
391
392
393
394
395
396 func (e Event) Thread() ThreadID {
397 return e.ctx.M
398 }
399
400
401
402
403
404 func (e Event) Stack() Stack {
405 if e.base.typ == evSync {
406 return NoStack
407 }
408 if e.base.typ == go122.EvCPUSample {
409 return Stack{table: e.table, id: stackID(e.base.args[0])}
410 }
411 spec := go122.Specs()[e.base.typ]
412 if len(spec.StackIDs) == 0 {
413 return NoStack
414 }
415
416
417
418 id := stackID(e.base.args[spec.StackIDs[0]-1])
419 if id == 0 {
420 return NoStack
421 }
422 return Stack{table: e.table, id: id}
423 }
424
425
426
427
428 func (e Event) Metric() Metric {
429 if e.Kind() != EventMetric {
430 panic("Metric called on non-Metric event")
431 }
432 var m Metric
433 switch e.base.typ {
434 case go122.EvProcsChange:
435 m.Name = "/sched/gomaxprocs:threads"
436 m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]}
437 case go122.EvHeapAlloc:
438 m.Name = "/memory/classes/heap/objects:bytes"
439 m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]}
440 case go122.EvHeapGoal:
441 m.Name = "/gc/heap/goal:bytes"
442 m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]}
443 default:
444 panic(fmt.Sprintf("internal error: unexpected event type for Metric kind: %s", go122.EventString(e.base.typ)))
445 }
446 return m
447 }
448
449
450
451
452 func (e Event) Label() Label {
453 if e.Kind() != EventLabel {
454 panic("Label called on non-Label event")
455 }
456 if e.base.typ != go122.EvGoLabel {
457 panic(fmt.Sprintf("internal error: unexpected event type for Label kind: %s", go122.EventString(e.base.typ)))
458 }
459 return Label{
460 Label: e.table.strings.mustGet(stringID(e.base.args[0])),
461 Resource: ResourceID{Kind: ResourceGoroutine, id: int64(e.ctx.G)},
462 }
463 }
464
465
466
467
468 func (e Event) Range() Range {
469 if kind := e.Kind(); kind != EventRangeBegin && kind != EventRangeActive && kind != EventRangeEnd {
470 panic("Range called on non-Range event")
471 }
472 var r Range
473 switch e.base.typ {
474 case go122.EvSTWBegin, go122.EvSTWEnd:
475
476
477 r.Name = "stop-the-world (" + e.table.strings.mustGet(stringID(e.base.args[0])) + ")"
478 r.Scope = ResourceID{Kind: ResourceGoroutine, id: int64(e.Goroutine())}
479 case go122.EvGCBegin, go122.EvGCActive, go122.EvGCEnd:
480 r.Name = "GC concurrent mark phase"
481 r.Scope = ResourceID{Kind: ResourceNone}
482 case go122.EvGCSweepBegin, go122.EvGCSweepActive, go122.EvGCSweepEnd:
483 r.Name = "GC incremental sweep"
484 r.Scope = ResourceID{Kind: ResourceProc}
485 if e.base.typ == go122.EvGCSweepActive {
486 r.Scope.id = int64(e.base.args[0])
487 } else {
488 r.Scope.id = int64(e.Proc())
489 }
490 r.Scope.id = int64(e.Proc())
491 case go122.EvGCMarkAssistBegin, go122.EvGCMarkAssistActive, go122.EvGCMarkAssistEnd:
492 r.Name = "GC mark assist"
493 r.Scope = ResourceID{Kind: ResourceGoroutine}
494 if e.base.typ == go122.EvGCMarkAssistActive {
495 r.Scope.id = int64(e.base.args[0])
496 } else {
497 r.Scope.id = int64(e.Goroutine())
498 }
499 default:
500 panic(fmt.Sprintf("internal error: unexpected event type for Range kind: %s", go122.EventString(e.base.typ)))
501 }
502 return r
503 }
504
505
506
507
508 func (e Event) RangeAttributes() []RangeAttribute {
509 if e.Kind() != EventRangeEnd {
510 panic("Range called on non-Range event")
511 }
512 if e.base.typ != go122.EvGCSweepEnd {
513 return nil
514 }
515 return []RangeAttribute{
516 {
517 Name: "bytes swept",
518 Value: Value{kind: ValueUint64, scalar: e.base.args[0]},
519 },
520 {
521 Name: "bytes reclaimed",
522 Value: Value{kind: ValueUint64, scalar: e.base.args[1]},
523 },
524 }
525 }
526
527
528
529
530 func (e Event) Task() Task {
531 if kind := e.Kind(); kind != EventTaskBegin && kind != EventTaskEnd {
532 panic("Task called on non-Task event")
533 }
534 parentID := NoTask
535 var typ string
536 switch e.base.typ {
537 case go122.EvUserTaskBegin:
538 parentID = TaskID(e.base.args[1])
539 typ = e.table.strings.mustGet(stringID(e.base.args[2]))
540 case go122.EvUserTaskEnd:
541 parentID = TaskID(e.base.extra(version.Go122)[0])
542 typ = e.table.getExtraString(extraStringID(e.base.extra(version.Go122)[1]))
543 default:
544 panic(fmt.Sprintf("internal error: unexpected event type for Task kind: %s", go122.EventString(e.base.typ)))
545 }
546 return Task{
547 ID: TaskID(e.base.args[0]),
548 Parent: parentID,
549 Type: typ,
550 }
551 }
552
553
554
555
556 func (e Event) Region() Region {
557 if kind := e.Kind(); kind != EventRegionBegin && kind != EventRegionEnd {
558 panic("Region called on non-Region event")
559 }
560 if e.base.typ != go122.EvUserRegionBegin && e.base.typ != go122.EvUserRegionEnd {
561 panic(fmt.Sprintf("internal error: unexpected event type for Region kind: %s", go122.EventString(e.base.typ)))
562 }
563 return Region{
564 Task: TaskID(e.base.args[0]),
565 Type: e.table.strings.mustGet(stringID(e.base.args[1])),
566 }
567 }
568
569
570
571
572 func (e Event) Log() Log {
573 if e.Kind() != EventLog {
574 panic("Log called on non-Log event")
575 }
576 if e.base.typ != go122.EvUserLog {
577 panic(fmt.Sprintf("internal error: unexpected event type for Log kind: %s", go122.EventString(e.base.typ)))
578 }
579 return Log{
580 Task: TaskID(e.base.args[0]),
581 Category: e.table.strings.mustGet(stringID(e.base.args[1])),
582 Message: e.table.strings.mustGet(stringID(e.base.args[2])),
583 }
584 }
585
586
587
588
589 func (e Event) StateTransition() StateTransition {
590 if e.Kind() != EventStateTransition {
591 panic("StateTransition called on non-StateTransition event")
592 }
593 var s StateTransition
594 switch e.base.typ {
595 case go122.EvProcStart:
596 s = procStateTransition(ProcID(e.base.args[0]), ProcIdle, ProcRunning)
597 case go122.EvProcStop:
598 s = procStateTransition(e.ctx.P, ProcRunning, ProcIdle)
599 case go122.EvProcSteal:
600
601 beforeState := ProcRunning
602 if go122.ProcStatus(e.base.extra(version.Go122)[0]) == go122.ProcSyscallAbandoned {
603
604
605
606
607 beforeState = ProcIdle
608 }
609 s = procStateTransition(ProcID(e.base.args[0]), beforeState, ProcIdle)
610 case go122.EvProcStatus:
611
612 s = procStateTransition(ProcID(e.base.args[0]), ProcState(e.base.extra(version.Go122)[0]), go122ProcStatus2ProcState[e.base.args[1]])
613 case go122.EvGoCreate, go122.EvGoCreateBlocked:
614 status := GoRunnable
615 if e.base.typ == go122.EvGoCreateBlocked {
616 status = GoWaiting
617 }
618 s = goStateTransition(GoID(e.base.args[0]), GoNotExist, status)
619 s.Stack = Stack{table: e.table, id: stackID(e.base.args[1])}
620 case go122.EvGoCreateSyscall:
621 s = goStateTransition(GoID(e.base.args[0]), GoNotExist, GoSyscall)
622 case go122.EvGoStart:
623 s = goStateTransition(GoID(e.base.args[0]), GoRunnable, GoRunning)
624 case go122.EvGoDestroy:
625 s = goStateTransition(e.ctx.G, GoRunning, GoNotExist)
626 s.Stack = e.Stack()
627 case go122.EvGoDestroySyscall:
628 s = goStateTransition(e.ctx.G, GoSyscall, GoNotExist)
629 case go122.EvGoStop:
630 s = goStateTransition(e.ctx.G, GoRunning, GoRunnable)
631 s.Reason = e.table.strings.mustGet(stringID(e.base.args[0]))
632 s.Stack = e.Stack()
633 case go122.EvGoBlock:
634 s = goStateTransition(e.ctx.G, GoRunning, GoWaiting)
635 s.Reason = e.table.strings.mustGet(stringID(e.base.args[0]))
636 s.Stack = e.Stack()
637 case go122.EvGoUnblock, go122.EvGoSwitch, go122.EvGoSwitchDestroy:
638
639
640
641 s = goStateTransition(GoID(e.base.args[0]), GoWaiting, GoRunnable)
642 case go122.EvGoSyscallBegin:
643 s = goStateTransition(e.ctx.G, GoRunning, GoSyscall)
644 s.Stack = e.Stack()
645 case go122.EvGoSyscallEnd:
646 s = goStateTransition(e.ctx.G, GoSyscall, GoRunning)
647 s.Stack = e.Stack()
648 case go122.EvGoSyscallEndBlocked:
649 s = goStateTransition(e.ctx.G, GoSyscall, GoRunnable)
650 s.Stack = e.Stack()
651 case go122.EvGoStatus, go122.EvGoStatusStack:
652 packedStatus := e.base.args[2]
653 from, to := packedStatus>>32, packedStatus&((1<<32)-1)
654 s = goStateTransition(GoID(e.base.args[0]), GoState(from), go122GoStatus2GoState[to])
655 default:
656 panic(fmt.Sprintf("internal error: unexpected event type for StateTransition kind: %s", go122.EventString(e.base.typ)))
657 }
658 return s
659 }
660
661
662
663
664 func (e Event) Experimental() ExperimentalEvent {
665 if e.Kind() != EventExperimental {
666 panic("Experimental called on non-Experimental event")
667 }
668 spec := go122.Specs()[e.base.typ]
669 argNames := spec.Args[1:]
670 return ExperimentalEvent{
671 Name: spec.Name,
672 ArgNames: argNames,
673 Args: e.base.args[:len(argNames)],
674 Data: e.table.expData[spec.Experiment],
675 }
676 }
677
678 const evSync = ^event.Type(0)
679
680 var go122Type2Kind = [...]EventKind{
681 go122.EvCPUSample: EventStackSample,
682 go122.EvProcsChange: EventMetric,
683 go122.EvProcStart: EventStateTransition,
684 go122.EvProcStop: EventStateTransition,
685 go122.EvProcSteal: EventStateTransition,
686 go122.EvProcStatus: EventStateTransition,
687 go122.EvGoCreate: EventStateTransition,
688 go122.EvGoCreateSyscall: EventStateTransition,
689 go122.EvGoStart: EventStateTransition,
690 go122.EvGoDestroy: EventStateTransition,
691 go122.EvGoDestroySyscall: EventStateTransition,
692 go122.EvGoStop: EventStateTransition,
693 go122.EvGoBlock: EventStateTransition,
694 go122.EvGoUnblock: EventStateTransition,
695 go122.EvGoSyscallBegin: EventStateTransition,
696 go122.EvGoSyscallEnd: EventStateTransition,
697 go122.EvGoSyscallEndBlocked: EventStateTransition,
698 go122.EvGoStatus: EventStateTransition,
699 go122.EvSTWBegin: EventRangeBegin,
700 go122.EvSTWEnd: EventRangeEnd,
701 go122.EvGCActive: EventRangeActive,
702 go122.EvGCBegin: EventRangeBegin,
703 go122.EvGCEnd: EventRangeEnd,
704 go122.EvGCSweepActive: EventRangeActive,
705 go122.EvGCSweepBegin: EventRangeBegin,
706 go122.EvGCSweepEnd: EventRangeEnd,
707 go122.EvGCMarkAssistActive: EventRangeActive,
708 go122.EvGCMarkAssistBegin: EventRangeBegin,
709 go122.EvGCMarkAssistEnd: EventRangeEnd,
710 go122.EvHeapAlloc: EventMetric,
711 go122.EvHeapGoal: EventMetric,
712 go122.EvGoLabel: EventLabel,
713 go122.EvUserTaskBegin: EventTaskBegin,
714 go122.EvUserTaskEnd: EventTaskEnd,
715 go122.EvUserRegionBegin: EventRegionBegin,
716 go122.EvUserRegionEnd: EventRegionEnd,
717 go122.EvUserLog: EventLog,
718 go122.EvGoSwitch: EventStateTransition,
719 go122.EvGoSwitchDestroy: EventStateTransition,
720 go122.EvGoCreateBlocked: EventStateTransition,
721 go122.EvGoStatusStack: EventStateTransition,
722 go122.EvSpan: EventExperimental,
723 go122.EvSpanAlloc: EventExperimental,
724 go122.EvSpanFree: EventExperimental,
725 go122.EvHeapObject: EventExperimental,
726 go122.EvHeapObjectAlloc: EventExperimental,
727 go122.EvHeapObjectFree: EventExperimental,
728 go122.EvGoroutineStack: EventExperimental,
729 go122.EvGoroutineStackAlloc: EventExperimental,
730 go122.EvGoroutineStackFree: EventExperimental,
731 evSync: EventSync,
732 }
733
734 var go122GoStatus2GoState = [...]GoState{
735 go122.GoRunnable: GoRunnable,
736 go122.GoRunning: GoRunning,
737 go122.GoWaiting: GoWaiting,
738 go122.GoSyscall: GoSyscall,
739 }
740
741 var go122ProcStatus2ProcState = [...]ProcState{
742 go122.ProcRunning: ProcRunning,
743 go122.ProcIdle: ProcIdle,
744 go122.ProcSyscall: ProcRunning,
745 go122.ProcSyscallAbandoned: ProcIdle,
746 }
747
748
749
750
751 func (e Event) String() string {
752 var sb strings.Builder
753 fmt.Fprintf(&sb, "M=%d P=%d G=%d", e.Thread(), e.Proc(), e.Goroutine())
754 fmt.Fprintf(&sb, " %s Time=%d", e.Kind(), e.Time())
755
756 switch kind := e.Kind(); kind {
757 case EventMetric:
758 m := e.Metric()
759 fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, valueAsString(m.Value))
760 case EventLabel:
761 l := e.Label()
762 fmt.Fprintf(&sb, " Label=%q Resource=%s", l.Label, l.Resource)
763 case EventRangeBegin, EventRangeActive, EventRangeEnd:
764 r := e.Range()
765 fmt.Fprintf(&sb, " Name=%q Scope=%s", r.Name, r.Scope)
766 if kind == EventRangeEnd {
767 fmt.Fprintf(&sb, " Attributes=[")
768 for i, attr := range e.RangeAttributes() {
769 if i != 0 {
770 fmt.Fprintf(&sb, " ")
771 }
772 fmt.Fprintf(&sb, "%q=%s", attr.Name, valueAsString(attr.Value))
773 }
774 fmt.Fprintf(&sb, "]")
775 }
776 case EventTaskBegin, EventTaskEnd:
777 t := e.Task()
778 fmt.Fprintf(&sb, " ID=%d Parent=%d Type=%q", t.ID, t.Parent, t.Type)
779 case EventRegionBegin, EventRegionEnd:
780 r := e.Region()
781 fmt.Fprintf(&sb, " Task=%d Type=%q", r.Task, r.Type)
782 case EventLog:
783 l := e.Log()
784 fmt.Fprintf(&sb, " Task=%d Category=%q Message=%q", l.Task, l.Category, l.Message)
785 case EventStateTransition:
786 s := e.StateTransition()
787 fmt.Fprintf(&sb, " Resource=%s Reason=%q", s.Resource, s.Reason)
788 switch s.Resource.Kind {
789 case ResourceGoroutine:
790 id := s.Resource.Goroutine()
791 old, new := s.Goroutine()
792 fmt.Fprintf(&sb, " GoID=%d %s->%s", id, old, new)
793 case ResourceProc:
794 id := s.Resource.Proc()
795 old, new := s.Proc()
796 fmt.Fprintf(&sb, " ProcID=%d %s->%s", id, old, new)
797 }
798 if s.Stack != NoStack {
799 fmt.Fprintln(&sb)
800 fmt.Fprintln(&sb, "TransitionStack=")
801 for f := range s.Stack.Frames() {
802 fmt.Fprintf(&sb, "\t%s @ 0x%x\n", f.Func, f.PC)
803 fmt.Fprintf(&sb, "\t\t%s:%d\n", f.File, f.Line)
804 }
805 }
806 case EventExperimental:
807 r := e.Experimental()
808 fmt.Fprintf(&sb, " Name=%s ArgNames=%v Args=%v", r.Name, r.ArgNames, r.Args)
809 }
810 if stk := e.Stack(); stk != NoStack {
811 fmt.Fprintln(&sb)
812 fmt.Fprintln(&sb, "Stack=")
813 for f := range stk.Frames() {
814 fmt.Fprintf(&sb, "\t%s @ 0x%x\n", f.Func, f.PC)
815 fmt.Fprintf(&sb, "\t\t%s:%d\n", f.File, f.Line)
816 }
817 }
818 return sb.String()
819 }
820
821
822
823 func (e Event) validateTableIDs() error {
824 if e.base.typ == evSync {
825 return nil
826 }
827 spec := go122.Specs()[e.base.typ]
828
829
830 for _, i := range spec.StackIDs {
831 id := stackID(e.base.args[i-1])
832 _, ok := e.table.stacks.get(id)
833 if !ok {
834 return fmt.Errorf("found invalid stack ID %d for event %s", id, spec.Name)
835 }
836 }
837
838
839
840
841 for _, i := range spec.StringIDs {
842 id := stringID(e.base.args[i-1])
843 _, ok := e.table.strings.get(id)
844 if !ok {
845 return fmt.Errorf("found invalid string ID %d for event %s", id, spec.Name)
846 }
847 }
848 return nil
849 }
850
851 func syncEvent(table *evTable, ts Time) Event {
852 return Event{
853 table: table,
854 ctx: schedCtx{
855 G: NoGoroutine,
856 P: NoProc,
857 M: NoThread,
858 },
859 base: baseEvent{
860 typ: evSync,
861 time: ts,
862 },
863 }
864 }
865
View as plain text