1
2
3
4
5 package trace
6
7 import (
8 "errors"
9 "fmt"
10 "io"
11 "iter"
12 "math"
13 "regexp"
14 "slices"
15 "strconv"
16 "strings"
17 "time"
18
19 "internal/trace/tracev2"
20 "internal/trace/version"
21 )
22
23
24
25
26
27 type EventKind uint16
28
29 const (
30 EventBad EventKind = iota
31
32
33
34
35
36 EventSync
37
38
39
40 EventMetric
41
42
43 EventLabel
44
45
46
47
48
49
50
51
52
53 EventStackSample
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 EventRangeBegin
70 EventRangeActive
71 EventRangeEnd
72
73
74 EventTaskBegin
75 EventTaskEnd
76
77
78 EventRegionBegin
79 EventRegionEnd
80
81
82 EventLog
83
84
85 EventStateTransition
86
87
88
89
90 EventExperimental
91 )
92
93
94 func (e EventKind) String() string {
95 if int(e) >= len(eventKindStrings) {
96 return eventKindStrings[0]
97 }
98 return eventKindStrings[e]
99 }
100
101 var eventKindStrings = [...]string{
102 EventBad: "Bad",
103 EventSync: "Sync",
104 EventMetric: "Metric",
105 EventLabel: "Label",
106 EventStackSample: "StackSample",
107 EventRangeBegin: "RangeBegin",
108 EventRangeActive: "RangeActive",
109 EventRangeEnd: "RangeEnd",
110 EventTaskBegin: "TaskBegin",
111 EventTaskEnd: "TaskEnd",
112 EventRegionBegin: "RegionBegin",
113 EventRegionEnd: "RegionEnd",
114 EventLog: "Log",
115 EventStateTransition: "StateTransition",
116 EventExperimental: "Experimental",
117 }
118
119 const maxTime = Time(math.MaxInt64)
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 type Time int64
141
142
143 func (t Time) Sub(t0 Time) time.Duration {
144 return time.Duration(int64(t) - int64(t0))
145 }
146
147
148 type Metric struct {
149
150
151
152
153
154
155
156
157 Name string
158
159
160
161
162
163 Value Value
164 }
165
166
167 type Label struct {
168
169 Label string
170
171
172 Resource ResourceID
173 }
174
175
176 type Range struct {
177
178
179
180
181
182
183
184 Name string
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201 Scope ResourceID
202 }
203
204
205 type RangeAttribute struct {
206
207 Name string
208
209
210 Value Value
211 }
212
213
214
215 type TaskID uint64
216
217 const (
218
219 NoTask = TaskID(^uint64(0))
220
221
222
223 BackgroundTask = TaskID(0)
224 )
225
226
227 type Task struct {
228
229
230
231 ID TaskID
232
233
234 Parent TaskID
235
236
237
238
239 Type string
240 }
241
242
243 type Region struct {
244
245 Task TaskID
246
247
248 Type string
249 }
250
251
252 type Log struct {
253
254 Task TaskID
255
256
257 Category string
258
259
260 Message string
261 }
262
263
264
265 type StackSample struct{}
266
267
268 func MakeStack(frames []StackFrame) Stack {
269
270 tbl := &evTable{pcs: make(map[uint64]frame)}
271 tbl.strings.compactify()
272 tbl.stacks.compactify()
273 return Stack{table: tbl, id: addStack(tbl, frames)}
274 }
275
276
277
278
279
280 type Stack struct {
281 table *evTable
282 id stackID
283 }
284
285
286 func (s Stack) Frames() iter.Seq[StackFrame] {
287 return func(yield func(StackFrame) bool) {
288 if s.id == 0 {
289 return
290 }
291 stk := s.table.stacks.mustGet(s.id)
292 for _, pc := range stk.pcs {
293 f := s.table.pcs[pc]
294 sf := StackFrame{
295 PC: f.pc,
296 Func: s.table.strings.mustGet(f.funcID),
297 File: s.table.strings.mustGet(f.fileID),
298 Line: f.line,
299 }
300 if !yield(sf) {
301 return
302 }
303 }
304 }
305 }
306
307
308
309
310 func (s Stack) String() string {
311 var sb strings.Builder
312 printStack(&sb, "", s.Frames())
313 return sb.String()
314 }
315
316 func printStack(w io.Writer, prefix string, frames iter.Seq[StackFrame]) {
317 for f := range frames {
318 fmt.Fprintf(w, "%s%s @ 0x%x\n", prefix, f.Func, f.PC)
319 fmt.Fprintf(w, "%s\t%s:%d\n", prefix, f.File, f.Line)
320 }
321 }
322
323
324
325 var NoStack = Stack{}
326
327
328 type StackFrame struct {
329
330
331
332 PC uint64
333
334
335 Func string
336
337
338 File string
339
340
341 Line uint64
342 }
343
344
345 type ExperimentalEvent struct {
346
347 Name string
348
349
350 Experiment string
351
352
353 Args []string
354
355
356
357 table *evTable
358 argValues []uint64
359 }
360
361
362 func (e ExperimentalEvent) ArgValue(i int) Value {
363 if i < 0 || i >= len(e.Args) {
364 panic(fmt.Sprintf("experimental event argument index %d out of bounds [0, %d)", i, len(e.Args)))
365 }
366 if strings.HasSuffix(e.Args[i], "string") {
367 s := e.table.strings.mustGet(stringID(e.argValues[i]))
368 return StringValue(s)
369 }
370 return Uint64Value(e.argValues[i])
371 }
372
373
374 type ExperimentalBatch struct {
375
376 Thread ThreadID
377
378
379 Data []byte
380 }
381
382 type EventDetails interface {
383 Metric | Label | Range | StateTransition | Sync | Task | Region | Log | StackSample
384 }
385
386
387 type EventConfig[T EventDetails] struct {
388
389 Time Time
390
391
392 Kind EventKind
393
394
395 Goroutine GoID
396
397
398 Proc ProcID
399
400
401 Thread ThreadID
402
403
404 Stack Stack
405
406
407 Details T
408 }
409
410
411 func MakeEvent[T EventDetails](c EventConfig[T]) (e Event, err error) {
412
413 e = Event{
414 table: &evTable{pcs: make(map[uint64]frame), sync: sync{freq: 1}},
415 base: baseEvent{time: c.Time},
416 ctx: schedCtx{G: c.Goroutine, P: c.Proc, M: c.Thread},
417 }
418 defer func() {
419
420 if err != nil || e.base.typ == evSync {
421 return
422 }
423 spec := tracev2.Specs()[e.base.typ]
424 if len(spec.StackIDs) > 0 && c.Stack != NoStack {
425
426
427
428 e.base.args[spec.StackIDs[0]-1] = uint64(addStack(e.table, slices.Collect(c.Stack.Frames())))
429 }
430
431 e.table.strings.compactify()
432 e.table.stacks.compactify()
433 }()
434 var defaultKind EventKind
435 switch c.Kind {
436 case defaultKind:
437 return Event{}, fmt.Errorf("the Kind field must be provided")
438 case EventMetric:
439 if m, ok := any(c.Details).(Metric); ok {
440 return makeMetricEvent(e, m)
441 }
442 case EventLabel:
443 if l, ok := any(c.Details).(Label); ok {
444 return makeLabelEvent(e, l)
445 }
446 case EventRangeBegin, EventRangeActive, EventRangeEnd:
447 if r, ok := any(c.Details).(Range); ok {
448 return makeRangeEvent(e, c.Kind, r)
449 }
450 case EventStateTransition:
451 if t, ok := any(c.Details).(StateTransition); ok {
452 return makeStateTransitionEvent(e, t)
453 }
454 case EventSync:
455 if s, ok := any(c.Details).(Sync); ok {
456 return makeSyncEvent(e, s)
457 }
458 case EventTaskBegin, EventTaskEnd:
459 if t, ok := any(c.Details).(Task); ok {
460 return makeTaskEvent(e, c.Kind, t)
461 }
462 case EventRegionBegin, EventRegionEnd:
463 if r, ok := any(c.Details).(Region); ok {
464 return makeRegionEvent(e, c.Kind, r)
465 }
466 case EventLog:
467 if l, ok := any(c.Details).(Log); ok {
468 return makeLogEvent(e, l)
469 }
470 case EventStackSample:
471 if _, ok := any(c.Details).(StackSample); ok {
472 return makeStackSampleEvent(e, c.Stack)
473 }
474 }
475 return Event{}, fmt.Errorf("the Kind field %s is incompatible with Details type %T", c.Kind, c.Details)
476 }
477
478 func makeMetricEvent(e Event, m Metric) (Event, error) {
479 if m.Value.Kind() != ValueUint64 {
480 return Event{}, fmt.Errorf("metric value must be a uint64, got: %s", m.Value.String())
481 }
482 switch m.Name {
483 case "/sched/gomaxprocs:threads":
484 e.base.typ = tracev2.EvProcsChange
485 case "/memory/classes/heap/objects:bytes":
486 e.base.typ = tracev2.EvHeapAlloc
487 case "/gc/heap/goal:bytes":
488 e.base.typ = tracev2.EvHeapGoal
489 default:
490 return Event{}, fmt.Errorf("unknown metric name: %s", m.Name)
491 }
492 e.base.args[0] = uint64(m.Value.Uint64())
493 return e, nil
494 }
495
496 func makeLabelEvent(e Event, l Label) (Event, error) {
497 if l.Resource.Kind != ResourceGoroutine {
498 return Event{}, fmt.Errorf("resource must be a goroutine: %s", l.Resource)
499 }
500 e.base.typ = tracev2.EvGoLabel
501 e.base.args[0] = uint64(e.table.strings.append(l.Label))
502
503 e.ctx.G = l.Resource.Goroutine()
504 return e, nil
505 }
506
507 var stwRangeRegexp = regexp.MustCompile(`^stop-the-world \((.*)\)$`)
508
509
510 func makeRangeEvent(e Event, kind EventKind, r Range) (Event, error) {
511
512
513 switch r.Name {
514 case "GC concurrent mark phase":
515 if r.Scope.Kind != ResourceNone {
516 return Event{}, fmt.Errorf("unexpected scope: %s", r.Scope)
517 }
518 switch kind {
519 case EventRangeBegin:
520 e.base.typ = tracev2.EvGCBegin
521 case EventRangeActive:
522 e.base.typ = tracev2.EvGCActive
523 case EventRangeEnd:
524 e.base.typ = tracev2.EvGCEnd
525 default:
526 return Event{}, fmt.Errorf("unexpected range kind: %s", kind)
527 }
528 case "GC incremental sweep":
529 if r.Scope.Kind != ResourceProc {
530 return Event{}, fmt.Errorf("unexpected scope: %s", r.Scope)
531 }
532 switch kind {
533 case EventRangeBegin:
534 e.base.typ = tracev2.EvGCSweepBegin
535 e.ctx.P = r.Scope.Proc()
536 case EventRangeActive:
537 e.base.typ = tracev2.EvGCSweepActive
538 e.base.args[0] = uint64(r.Scope.Proc())
539 case EventRangeEnd:
540 e.base.typ = tracev2.EvGCSweepEnd
541
542 e.ctx.P = r.Scope.Proc()
543 default:
544 return Event{}, fmt.Errorf("unexpected range kind: %s", kind)
545 }
546 case "GC mark assist":
547 if r.Scope.Kind != ResourceGoroutine {
548 return Event{}, fmt.Errorf("unexpected scope: %s", r.Scope)
549 }
550 switch kind {
551 case EventRangeBegin:
552 e.base.typ = tracev2.EvGCMarkAssistBegin
553 e.ctx.G = r.Scope.Goroutine()
554 case EventRangeActive:
555 e.base.typ = tracev2.EvGCMarkAssistActive
556 e.base.args[0] = uint64(r.Scope.Goroutine())
557 case EventRangeEnd:
558 e.base.typ = tracev2.EvGCMarkAssistEnd
559
560 e.ctx.G = r.Scope.Goroutine()
561 default:
562 return Event{}, fmt.Errorf("unexpected range kind: %s", kind)
563 }
564 default:
565 match := stwRangeRegexp.FindStringSubmatch(r.Name)
566 if len(match) != 2 {
567 return Event{}, fmt.Errorf("unexpected range name: %s", r.Name)
568 }
569 if r.Scope.Kind != ResourceGoroutine {
570 return Event{}, fmt.Errorf("unexpected scope: %s", r.Scope)
571 }
572 switch kind {
573 case EventRangeBegin:
574 e.base.typ = tracev2.EvSTWBegin
575
576 e.ctx.G = r.Scope.Goroutine()
577 case EventRangeEnd:
578 e.base.typ = tracev2.EvSTWEnd
579
580 e.ctx.G = r.Scope.Goroutine()
581 default:
582 return Event{}, fmt.Errorf("unexpected range kind: %s", kind)
583 }
584 e.base.args[0] = uint64(e.table.strings.append(match[1]))
585 }
586 return e, nil
587 }
588
589 func makeStateTransitionEvent(e Event, t StateTransition) (Event, error) {
590 switch t.Resource.Kind {
591 case ResourceProc:
592 from, to := ProcState(t.oldState), ProcState(t.newState)
593 switch {
594 case from == ProcIdle && to == ProcIdle:
595
596 e.base.typ = tracev2.EvProcSteal
597 e.base.args[0] = uint64(t.Resource.Proc())
598 e.base.extra(version.Go122)[0] = uint64(tracev2.ProcSyscallAbandoned)
599 case from == ProcIdle && to == ProcRunning:
600 e.base.typ = tracev2.EvProcStart
601 e.base.args[0] = uint64(t.Resource.Proc())
602 case from == ProcRunning && to == ProcIdle:
603 e.base.typ = tracev2.EvProcStop
604 if t.Resource.Proc() != e.ctx.P {
605 e.base.typ = tracev2.EvProcSteal
606 e.base.args[0] = uint64(t.Resource.Proc())
607 }
608 default:
609 e.base.typ = tracev2.EvProcStatus
610 e.base.args[0] = uint64(t.Resource.Proc())
611 e.base.args[1] = uint64(procState2Tracev2ProcStatus[to])
612 e.base.extra(version.Go122)[0] = uint64(procState2Tracev2ProcStatus[from])
613 return e, nil
614 }
615 case ResourceGoroutine:
616 from, to := GoState(t.oldState), GoState(t.newState)
617 stack := slices.Collect(t.Stack.Frames())
618 goroutine := t.Resource.Goroutine()
619
620 if (from == GoUndetermined || from == to) && from != GoNotExist {
621 e.base.typ = tracev2.EvGoStatus
622 if len(stack) > 0 {
623 e.base.typ = tracev2.EvGoStatusStack
624 }
625 e.base.args[0] = uint64(goroutine)
626 e.base.args[2] = uint64(from)<<32 | uint64(goState2Tracev2GoStatus[to])
627 } else {
628 switch from {
629 case GoNotExist:
630 switch to {
631 case GoWaiting:
632 e.base.typ = tracev2.EvGoCreateBlocked
633 e.base.args[0] = uint64(goroutine)
634 e.base.args[1] = uint64(addStack(e.table, stack))
635 case GoRunnable:
636 e.base.typ = tracev2.EvGoCreate
637 e.base.args[0] = uint64(goroutine)
638 e.base.args[1] = uint64(addStack(e.table, stack))
639 case GoSyscall:
640 e.base.typ = tracev2.EvGoCreateSyscall
641 e.base.args[0] = uint64(goroutine)
642 default:
643 return Event{}, fmt.Errorf("unexpected transition: %s -> %s", from, to)
644 }
645 case GoRunnable:
646 e.base.typ = tracev2.EvGoStart
647 e.base.args[0] = uint64(goroutine)
648 case GoRunning:
649 switch to {
650 case GoNotExist:
651 e.base.typ = tracev2.EvGoDestroy
652 e.ctx.G = goroutine
653 case GoRunnable:
654 e.base.typ = tracev2.EvGoStop
655 e.ctx.G = goroutine
656 e.base.args[0] = uint64(e.table.strings.append(t.Reason))
657 case GoWaiting:
658 e.base.typ = tracev2.EvGoBlock
659 e.ctx.G = goroutine
660 e.base.args[0] = uint64(e.table.strings.append(t.Reason))
661 case GoSyscall:
662 e.base.typ = tracev2.EvGoSyscallBegin
663 e.ctx.G = goroutine
664 default:
665 return Event{}, fmt.Errorf("unexpected transition: %s -> %s", from, to)
666 }
667 case GoSyscall:
668 switch to {
669 case GoNotExist:
670 e.base.typ = tracev2.EvGoDestroySyscall
671 e.ctx.G = goroutine
672 case GoRunning:
673 e.base.typ = tracev2.EvGoSyscallEnd
674 e.ctx.G = goroutine
675 case GoRunnable:
676 e.base.typ = tracev2.EvGoSyscallEndBlocked
677 e.ctx.G = goroutine
678 default:
679 return Event{}, fmt.Errorf("unexpected transition: %s -> %s", from, to)
680 }
681 case GoWaiting:
682 switch to {
683 case GoRunnable:
684 e.base.typ = tracev2.EvGoUnblock
685 e.base.args[0] = uint64(goroutine)
686 default:
687 return Event{}, fmt.Errorf("unexpected transition: %s -> %s", from, to)
688 }
689 default:
690 return Event{}, fmt.Errorf("unexpected transition: %s -> %s", from, to)
691 }
692 }
693 default:
694 return Event{}, fmt.Errorf("unsupported state transition resource: %s", t.Resource)
695 }
696 return e, nil
697 }
698
699 func makeSyncEvent(e Event, s Sync) (Event, error) {
700 e.base.typ = evSync
701 e.base.args[0] = uint64(s.N)
702 if e.table.expBatches == nil {
703 e.table.expBatches = make(map[tracev2.Experiment][]ExperimentalBatch)
704 }
705 for name, batches := range s.ExperimentalBatches {
706 var found bool
707 for id, exp := range tracev2.Experiments() {
708 if exp == name {
709 found = true
710 e.table.expBatches[tracev2.Experiment(id)] = batches
711 break
712 }
713 }
714 if !found {
715 return Event{}, fmt.Errorf("unknown experiment: %s", name)
716 }
717 }
718 if s.ClockSnapshot != nil {
719 e.table.hasClockSnapshot = true
720 e.table.snapWall = s.ClockSnapshot.Wall
721 e.table.snapMono = s.ClockSnapshot.Mono
722
723 e.table.snapTime = timestamp(s.ClockSnapshot.Trace)
724 }
725 return e, nil
726 }
727
728 func makeTaskEvent(e Event, kind EventKind, t Task) (Event, error) {
729 if t.ID == NoTask {
730 return Event{}, errors.New("task ID cannot be NoTask")
731 }
732 e.base.args[0] = uint64(t.ID)
733 switch kind {
734 case EventTaskBegin:
735 e.base.typ = tracev2.EvUserTaskBegin
736 e.base.args[1] = uint64(t.Parent)
737 e.base.args[2] = uint64(e.table.strings.append(t.Type))
738 case EventTaskEnd:
739 e.base.typ = tracev2.EvUserTaskEnd
740 e.base.extra(version.Go122)[0] = uint64(t.Parent)
741 e.base.extra(version.Go122)[1] = uint64(e.table.addExtraString(t.Type))
742 default:
743
744 panic("unexpected task kind")
745 }
746 return e, nil
747 }
748
749 func makeRegionEvent(e Event, kind EventKind, r Region) (Event, error) {
750 e.base.args[0] = uint64(r.Task)
751 e.base.args[1] = uint64(e.table.strings.append(r.Type))
752 switch kind {
753 case EventRegionBegin:
754 e.base.typ = tracev2.EvUserRegionBegin
755 case EventRegionEnd:
756 e.base.typ = tracev2.EvUserRegionEnd
757 default:
758 panic("unexpected region kind")
759 }
760 return e, nil
761 }
762
763 func makeLogEvent(e Event, l Log) (Event, error) {
764 e.base.typ = tracev2.EvUserLog
765 e.base.args[0] = uint64(l.Task)
766 e.base.args[1] = uint64(e.table.strings.append(l.Category))
767 e.base.args[2] = uint64(e.table.strings.append(l.Message))
768 return e, nil
769 }
770
771 func makeStackSampleEvent(e Event, s Stack) (Event, error) {
772 e.base.typ = tracev2.EvCPUSample
773 frames := slices.Collect(s.Frames())
774 e.base.args[0] = uint64(addStack(e.table, frames))
775 return e, nil
776 }
777
778 func addStack(table *evTable, frames []StackFrame) stackID {
779 var pcs []uint64
780 for _, f := range frames {
781 table.pcs[f.PC] = frame{
782 pc: f.PC,
783 funcID: table.strings.append(f.Func),
784 fileID: table.strings.append(f.File),
785 line: f.Line,
786 }
787 pcs = append(pcs, f.PC)
788 }
789 return table.stacks.append(stack{pcs: pcs})
790 }
791
792
793 type Event struct {
794 table *evTable
795 ctx schedCtx
796 base baseEvent
797 }
798
799
800 func (e Event) Kind() EventKind {
801 return tracev2Type2Kind[e.base.typ]
802 }
803
804
805 func (e Event) Time() Time {
806 return e.base.time
807 }
808
809
810
811
812
813
814
815
816
817
818 func (e Event) Goroutine() GoID {
819 return e.ctx.G
820 }
821
822
823
824
825
826
827 func (e Event) Proc() ProcID {
828 return e.ctx.P
829 }
830
831
832
833
834
835
836
837
838
839
840
841 func (e Event) Thread() ThreadID {
842 return e.ctx.M
843 }
844
845
846
847
848
849 func (e Event) Stack() Stack {
850 if e.base.typ == evSync {
851 return NoStack
852 }
853 if e.base.typ == tracev2.EvCPUSample {
854 return Stack{table: e.table, id: stackID(e.base.args[0])}
855 }
856 spec := tracev2.Specs()[e.base.typ]
857 if len(spec.StackIDs) == 0 {
858 return NoStack
859 }
860
861
862
863 id := stackID(e.base.args[spec.StackIDs[0]-1])
864 if id == 0 {
865 return NoStack
866 }
867 return Stack{table: e.table, id: id}
868 }
869
870
871
872
873 func (e Event) Metric() Metric {
874 if e.Kind() != EventMetric {
875 panic("Metric called on non-Metric event")
876 }
877 var m Metric
878 switch e.base.typ {
879 case tracev2.EvProcsChange:
880 m.Name = "/sched/gomaxprocs:threads"
881 m.Value = Uint64Value(e.base.args[0])
882 case tracev2.EvHeapAlloc:
883 m.Name = "/memory/classes/heap/objects:bytes"
884 m.Value = Uint64Value(e.base.args[0])
885 case tracev2.EvHeapGoal:
886 m.Name = "/gc/heap/goal:bytes"
887 m.Value = Uint64Value(e.base.args[0])
888 default:
889 panic(fmt.Sprintf("internal error: unexpected wire-format event type for Metric kind: %d", e.base.typ))
890 }
891 return m
892 }
893
894
895
896
897 func (e Event) Label() Label {
898 if e.Kind() != EventLabel {
899 panic("Label called on non-Label event")
900 }
901 if e.base.typ != tracev2.EvGoLabel {
902 panic(fmt.Sprintf("internal error: unexpected wire-format event type for Label kind: %d", e.base.typ))
903 }
904 return Label{
905 Label: e.table.strings.mustGet(stringID(e.base.args[0])),
906 Resource: ResourceID{Kind: ResourceGoroutine, id: int64(e.ctx.G)},
907 }
908 }
909
910
911
912
913 func (e Event) Range() Range {
914 if kind := e.Kind(); kind != EventRangeBegin && kind != EventRangeActive && kind != EventRangeEnd {
915 panic("Range called on non-Range event")
916 }
917 var r Range
918 switch e.base.typ {
919 case tracev2.EvSTWBegin, tracev2.EvSTWEnd:
920
921
922 r.Name = "stop-the-world (" + e.table.strings.mustGet(stringID(e.base.args[0])) + ")"
923 r.Scope = ResourceID{Kind: ResourceGoroutine, id: int64(e.Goroutine())}
924 case tracev2.EvGCBegin, tracev2.EvGCActive, tracev2.EvGCEnd:
925 r.Name = "GC concurrent mark phase"
926 r.Scope = ResourceID{Kind: ResourceNone}
927 case tracev2.EvGCSweepBegin, tracev2.EvGCSweepActive, tracev2.EvGCSweepEnd:
928 r.Name = "GC incremental sweep"
929 r.Scope = ResourceID{Kind: ResourceProc}
930 if e.base.typ == tracev2.EvGCSweepActive {
931 r.Scope.id = int64(e.base.args[0])
932 } else {
933 r.Scope.id = int64(e.Proc())
934 }
935 case tracev2.EvGCMarkAssistBegin, tracev2.EvGCMarkAssistActive, tracev2.EvGCMarkAssistEnd:
936 r.Name = "GC mark assist"
937 r.Scope = ResourceID{Kind: ResourceGoroutine}
938 if e.base.typ == tracev2.EvGCMarkAssistActive {
939 r.Scope.id = int64(e.base.args[0])
940 } else {
941 r.Scope.id = int64(e.Goroutine())
942 }
943 default:
944 panic(fmt.Sprintf("internal error: unexpected wire-event type for Range kind: %d", e.base.typ))
945 }
946 return r
947 }
948
949
950
951
952 func (e Event) RangeAttributes() []RangeAttribute {
953 if e.Kind() != EventRangeEnd {
954 panic("Range called on non-Range event")
955 }
956 if e.base.typ != tracev2.EvGCSweepEnd {
957 return nil
958 }
959 return []RangeAttribute{
960 {
961 Name: "bytes swept",
962 Value: Uint64Value(e.base.args[0]),
963 },
964 {
965 Name: "bytes reclaimed",
966 Value: Uint64Value(e.base.args[1]),
967 },
968 }
969 }
970
971
972
973
974 func (e Event) Task() Task {
975 if kind := e.Kind(); kind != EventTaskBegin && kind != EventTaskEnd {
976 panic("Task called on non-Task event")
977 }
978 parentID := NoTask
979 var typ string
980 switch e.base.typ {
981 case tracev2.EvUserTaskBegin:
982 parentID = TaskID(e.base.args[1])
983 typ = e.table.strings.mustGet(stringID(e.base.args[2]))
984 case tracev2.EvUserTaskEnd:
985 parentID = TaskID(e.base.extra(version.Go122)[0])
986 typ = e.table.getExtraString(extraStringID(e.base.extra(version.Go122)[1]))
987 default:
988 panic(fmt.Sprintf("internal error: unexpected wire-format event type for Task kind: %d", e.base.typ))
989 }
990 return Task{
991 ID: TaskID(e.base.args[0]),
992 Parent: parentID,
993 Type: typ,
994 }
995 }
996
997
998
999
1000 func (e Event) Region() Region {
1001 if kind := e.Kind(); kind != EventRegionBegin && kind != EventRegionEnd {
1002 panic("Region called on non-Region event")
1003 }
1004 if e.base.typ != tracev2.EvUserRegionBegin && e.base.typ != tracev2.EvUserRegionEnd {
1005 panic(fmt.Sprintf("internal error: unexpected wire-format event type for Region kind: %d", e.base.typ))
1006 }
1007 return Region{
1008 Task: TaskID(e.base.args[0]),
1009 Type: e.table.strings.mustGet(stringID(e.base.args[1])),
1010 }
1011 }
1012
1013
1014
1015
1016 func (e Event) Log() Log {
1017 if e.Kind() != EventLog {
1018 panic("Log called on non-Log event")
1019 }
1020 if e.base.typ != tracev2.EvUserLog {
1021 panic(fmt.Sprintf("internal error: unexpected wire-format event type for Log kind: %d", e.base.typ))
1022 }
1023 return Log{
1024 Task: TaskID(e.base.args[0]),
1025 Category: e.table.strings.mustGet(stringID(e.base.args[1])),
1026 Message: e.table.strings.mustGet(stringID(e.base.args[2])),
1027 }
1028 }
1029
1030
1031
1032
1033 func (e Event) StateTransition() StateTransition {
1034 if e.Kind() != EventStateTransition {
1035 panic("StateTransition called on non-StateTransition event")
1036 }
1037 var s StateTransition
1038 switch e.base.typ {
1039 case tracev2.EvProcStart:
1040 s = MakeProcStateTransition(ProcID(e.base.args[0]), ProcIdle, ProcRunning)
1041 case tracev2.EvProcStop:
1042 s = MakeProcStateTransition(e.ctx.P, ProcRunning, ProcIdle)
1043 case tracev2.EvProcSteal:
1044
1045 beforeState := ProcRunning
1046 if tracev2.ProcStatus(e.base.extra(version.Go122)[0]) == tracev2.ProcSyscallAbandoned {
1047
1048
1049
1050
1051 beforeState = ProcIdle
1052 }
1053 s = MakeProcStateTransition(ProcID(e.base.args[0]), beforeState, ProcIdle)
1054 case tracev2.EvProcStatus:
1055
1056 s = MakeProcStateTransition(ProcID(e.base.args[0]), ProcState(e.base.extra(version.Go122)[0]), tracev2ProcStatus2ProcState[e.base.args[1]])
1057 case tracev2.EvGoCreate, tracev2.EvGoCreateBlocked:
1058 status := GoRunnable
1059 if e.base.typ == tracev2.EvGoCreateBlocked {
1060 status = GoWaiting
1061 }
1062 s = MakeGoStateTransition(GoID(e.base.args[0]), GoNotExist, status)
1063 s.Stack = Stack{table: e.table, id: stackID(e.base.args[1])}
1064 case tracev2.EvGoCreateSyscall:
1065 s = MakeGoStateTransition(GoID(e.base.args[0]), GoNotExist, GoSyscall)
1066 case tracev2.EvGoStart:
1067 s = MakeGoStateTransition(GoID(e.base.args[0]), GoRunnable, GoRunning)
1068 case tracev2.EvGoDestroy:
1069 s = MakeGoStateTransition(e.ctx.G, GoRunning, GoNotExist)
1070 case tracev2.EvGoDestroySyscall:
1071 s = MakeGoStateTransition(e.ctx.G, GoSyscall, GoNotExist)
1072 case tracev2.EvGoStop:
1073 s = MakeGoStateTransition(e.ctx.G, GoRunning, GoRunnable)
1074 s.Reason = e.table.strings.mustGet(stringID(e.base.args[0]))
1075 s.Stack = e.Stack()
1076 case tracev2.EvGoBlock:
1077 s = MakeGoStateTransition(e.ctx.G, GoRunning, GoWaiting)
1078 s.Reason = e.table.strings.mustGet(stringID(e.base.args[0]))
1079 s.Stack = e.Stack()
1080 case tracev2.EvGoUnblock, tracev2.EvGoSwitch, tracev2.EvGoSwitchDestroy:
1081
1082
1083
1084 s = MakeGoStateTransition(GoID(e.base.args[0]), GoWaiting, GoRunnable)
1085 case tracev2.EvGoSyscallBegin:
1086 s = MakeGoStateTransition(e.ctx.G, GoRunning, GoSyscall)
1087 s.Stack = e.Stack()
1088 case tracev2.EvGoSyscallEnd:
1089 s = MakeGoStateTransition(e.ctx.G, GoSyscall, GoRunning)
1090 case tracev2.EvGoSyscallEndBlocked:
1091 s = MakeGoStateTransition(e.ctx.G, GoSyscall, GoRunnable)
1092 case tracev2.EvGoStatus, tracev2.EvGoStatusStack:
1093 packedStatus := e.base.args[2]
1094 from, to := packedStatus>>32, packedStatus&((1<<32)-1)
1095 s = MakeGoStateTransition(GoID(e.base.args[0]), GoState(from), tracev2GoStatus2GoState[to])
1096 s.Stack = e.Stack()
1097 default:
1098 panic(fmt.Sprintf("internal error: unexpected wire-format event type for StateTransition kind: %d", e.base.typ))
1099 }
1100 return s
1101 }
1102
1103
1104
1105 func (e Event) Sync() Sync {
1106 if e.Kind() != EventSync {
1107 panic("Sync called on non-Sync event")
1108 }
1109 s := Sync{N: int(e.base.args[0])}
1110 if e.table != nil {
1111 expBatches := make(map[string][]ExperimentalBatch)
1112 for exp, batches := range e.table.expBatches {
1113 expBatches[tracev2.Experiments()[exp]] = batches
1114 }
1115 s.ExperimentalBatches = expBatches
1116 if e.table.hasClockSnapshot {
1117 s.ClockSnapshot = &ClockSnapshot{
1118 Trace: e.table.freq.mul(e.table.snapTime),
1119 Wall: e.table.snapWall,
1120 Mono: e.table.snapMono,
1121 }
1122 }
1123 }
1124 return s
1125 }
1126
1127
1128
1129 type Sync struct {
1130
1131 N int
1132
1133
1134
1135
1136
1137
1138 ClockSnapshot *ClockSnapshot
1139
1140
1141 ExperimentalBatches map[string][]ExperimentalBatch
1142 }
1143
1144
1145
1146
1147
1148 type ClockSnapshot struct {
1149
1150 Trace Time
1151
1152
1153 Wall time.Time
1154
1155
1156 Mono uint64
1157 }
1158
1159
1160
1161
1162 func (e Event) Experimental() ExperimentalEvent {
1163 if e.Kind() != EventExperimental {
1164 panic("Experimental called on non-Experimental event")
1165 }
1166 spec := tracev2.Specs()[e.base.typ]
1167 argNames := spec.Args[1:]
1168 return ExperimentalEvent{
1169 Name: spec.Name,
1170 Experiment: tracev2.Experiments()[spec.Experiment],
1171 Args: argNames,
1172 table: e.table,
1173 argValues: e.base.args[:len(argNames)],
1174 }
1175 }
1176
1177 const evSync = ^tracev2.EventType(0)
1178
1179 var tracev2Type2Kind = [...]EventKind{
1180 tracev2.EvCPUSample: EventStackSample,
1181 tracev2.EvProcsChange: EventMetric,
1182 tracev2.EvProcStart: EventStateTransition,
1183 tracev2.EvProcStop: EventStateTransition,
1184 tracev2.EvProcSteal: EventStateTransition,
1185 tracev2.EvProcStatus: EventStateTransition,
1186 tracev2.EvGoCreate: EventStateTransition,
1187 tracev2.EvGoCreateSyscall: EventStateTransition,
1188 tracev2.EvGoStart: EventStateTransition,
1189 tracev2.EvGoDestroy: EventStateTransition,
1190 tracev2.EvGoDestroySyscall: EventStateTransition,
1191 tracev2.EvGoStop: EventStateTransition,
1192 tracev2.EvGoBlock: EventStateTransition,
1193 tracev2.EvGoUnblock: EventStateTransition,
1194 tracev2.EvGoSyscallBegin: EventStateTransition,
1195 tracev2.EvGoSyscallEnd: EventStateTransition,
1196 tracev2.EvGoSyscallEndBlocked: EventStateTransition,
1197 tracev2.EvGoStatus: EventStateTransition,
1198 tracev2.EvSTWBegin: EventRangeBegin,
1199 tracev2.EvSTWEnd: EventRangeEnd,
1200 tracev2.EvGCActive: EventRangeActive,
1201 tracev2.EvGCBegin: EventRangeBegin,
1202 tracev2.EvGCEnd: EventRangeEnd,
1203 tracev2.EvGCSweepActive: EventRangeActive,
1204 tracev2.EvGCSweepBegin: EventRangeBegin,
1205 tracev2.EvGCSweepEnd: EventRangeEnd,
1206 tracev2.EvGCMarkAssistActive: EventRangeActive,
1207 tracev2.EvGCMarkAssistBegin: EventRangeBegin,
1208 tracev2.EvGCMarkAssistEnd: EventRangeEnd,
1209 tracev2.EvHeapAlloc: EventMetric,
1210 tracev2.EvHeapGoal: EventMetric,
1211 tracev2.EvGoLabel: EventLabel,
1212 tracev2.EvUserTaskBegin: EventTaskBegin,
1213 tracev2.EvUserTaskEnd: EventTaskEnd,
1214 tracev2.EvUserRegionBegin: EventRegionBegin,
1215 tracev2.EvUserRegionEnd: EventRegionEnd,
1216 tracev2.EvUserLog: EventLog,
1217 tracev2.EvGoSwitch: EventStateTransition,
1218 tracev2.EvGoSwitchDestroy: EventStateTransition,
1219 tracev2.EvGoCreateBlocked: EventStateTransition,
1220 tracev2.EvGoStatusStack: EventStateTransition,
1221 tracev2.EvSpan: EventExperimental,
1222 tracev2.EvSpanAlloc: EventExperimental,
1223 tracev2.EvSpanFree: EventExperimental,
1224 tracev2.EvHeapObject: EventExperimental,
1225 tracev2.EvHeapObjectAlloc: EventExperimental,
1226 tracev2.EvHeapObjectFree: EventExperimental,
1227 tracev2.EvGoroutineStack: EventExperimental,
1228 tracev2.EvGoroutineStackAlloc: EventExperimental,
1229 tracev2.EvGoroutineStackFree: EventExperimental,
1230 evSync: EventSync,
1231 }
1232
1233 var tracev2GoStatus2GoState = [...]GoState{
1234 tracev2.GoRunnable: GoRunnable,
1235 tracev2.GoRunning: GoRunning,
1236 tracev2.GoWaiting: GoWaiting,
1237 tracev2.GoSyscall: GoSyscall,
1238 }
1239
1240 var goState2Tracev2GoStatus = [...]tracev2.GoStatus{
1241 GoRunnable: tracev2.GoRunnable,
1242 GoRunning: tracev2.GoRunning,
1243 GoWaiting: tracev2.GoWaiting,
1244 GoSyscall: tracev2.GoSyscall,
1245 }
1246
1247 var tracev2ProcStatus2ProcState = [...]ProcState{
1248 tracev2.ProcRunning: ProcRunning,
1249 tracev2.ProcIdle: ProcIdle,
1250 tracev2.ProcSyscall: ProcRunning,
1251 tracev2.ProcSyscallAbandoned: ProcIdle,
1252 }
1253
1254 var procState2Tracev2ProcStatus = [...]tracev2.ProcStatus{
1255 ProcRunning: tracev2.ProcRunning,
1256 ProcIdle: tracev2.ProcIdle,
1257
1258 }
1259
1260
1261
1262
1263 func (e Event) String() string {
1264 var sb strings.Builder
1265 fmt.Fprintf(&sb, "M=%d P=%d G=%d", e.Thread(), e.Proc(), e.Goroutine())
1266 fmt.Fprintf(&sb, " %s Time=%d", e.Kind(), e.Time())
1267
1268 switch kind := e.Kind(); kind {
1269 case EventMetric:
1270 m := e.Metric()
1271 v := m.Value.String()
1272 if m.Value.Kind() == ValueString {
1273 v = strconv.Quote(v)
1274 }
1275 fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, m.Value)
1276 case EventLabel:
1277 l := e.Label()
1278 fmt.Fprintf(&sb, " Label=%q Resource=%s", l.Label, l.Resource)
1279 case EventRangeBegin, EventRangeActive, EventRangeEnd:
1280 r := e.Range()
1281 fmt.Fprintf(&sb, " Name=%q Scope=%s", r.Name, r.Scope)
1282 if kind == EventRangeEnd {
1283 fmt.Fprintf(&sb, " Attributes=[")
1284 for i, attr := range e.RangeAttributes() {
1285 if i != 0 {
1286 fmt.Fprintf(&sb, " ")
1287 }
1288 fmt.Fprintf(&sb, "%q=%s", attr.Name, attr.Value)
1289 }
1290 fmt.Fprintf(&sb, "]")
1291 }
1292 case EventTaskBegin, EventTaskEnd:
1293 t := e.Task()
1294 fmt.Fprintf(&sb, " ID=%d Parent=%d Type=%q", t.ID, t.Parent, t.Type)
1295 case EventRegionBegin, EventRegionEnd:
1296 r := e.Region()
1297 fmt.Fprintf(&sb, " Task=%d Type=%q", r.Task, r.Type)
1298 case EventLog:
1299 l := e.Log()
1300 fmt.Fprintf(&sb, " Task=%d Category=%q Message=%q", l.Task, l.Category, l.Message)
1301 case EventStateTransition:
1302 s := e.StateTransition()
1303 switch s.Resource.Kind {
1304 case ResourceGoroutine:
1305 id := s.Resource.Goroutine()
1306 old, new := s.Goroutine()
1307 fmt.Fprintf(&sb, " GoID=%d %s->%s", id, old, new)
1308 case ResourceProc:
1309 id := s.Resource.Proc()
1310 old, new := s.Proc()
1311 fmt.Fprintf(&sb, " ProcID=%d %s->%s", id, old, new)
1312 }
1313 fmt.Fprintf(&sb, " Reason=%q", s.Reason)
1314 if s.Stack != NoStack {
1315 fmt.Fprintln(&sb)
1316 fmt.Fprintln(&sb, "TransitionStack=")
1317 printStack(&sb, "\t", s.Stack.Frames())
1318 }
1319 case EventExperimental:
1320 r := e.Experimental()
1321 fmt.Fprintf(&sb, " Name=%s Args=[", r.Name)
1322 for i, arg := range r.Args {
1323 if i != 0 {
1324 fmt.Fprintf(&sb, ", ")
1325 }
1326 fmt.Fprintf(&sb, "%s=%s", arg, r.ArgValue(i).String())
1327 }
1328 fmt.Fprintf(&sb, "]")
1329 case EventSync:
1330 s := e.Sync()
1331 fmt.Fprintf(&sb, " N=%d", s.N)
1332 if s.ClockSnapshot != nil {
1333 fmt.Fprintf(&sb, " Trace=%d Mono=%d Wall=%s",
1334 s.ClockSnapshot.Trace,
1335 s.ClockSnapshot.Mono,
1336 s.ClockSnapshot.Wall.Format(time.RFC3339Nano),
1337 )
1338 }
1339 }
1340 if stk := e.Stack(); stk != NoStack {
1341 fmt.Fprintln(&sb)
1342 fmt.Fprintln(&sb, "Stack=")
1343 printStack(&sb, "\t", stk.Frames())
1344 }
1345 return sb.String()
1346 }
1347
1348
1349
1350 func (e Event) validateTableIDs() error {
1351 if e.base.typ == evSync {
1352 return nil
1353 }
1354 spec := tracev2.Specs()[e.base.typ]
1355
1356
1357 for _, i := range spec.StackIDs {
1358 id := stackID(e.base.args[i-1])
1359 _, ok := e.table.stacks.get(id)
1360 if !ok {
1361 return fmt.Errorf("found invalid stack ID %d for event %s", id, spec.Name)
1362 }
1363 }
1364
1365
1366
1367
1368 for _, i := range spec.StringIDs {
1369 id := stringID(e.base.args[i-1])
1370 _, ok := e.table.strings.get(id)
1371 if !ok {
1372 return fmt.Errorf("found invalid string ID %d for event %s", id, spec.Name)
1373 }
1374 }
1375 return nil
1376 }
1377
1378 func syncEvent(table *evTable, ts Time, n int) Event {
1379 ev := Event{
1380 table: table,
1381 ctx: schedCtx{
1382 G: NoGoroutine,
1383 P: NoProc,
1384 M: NoThread,
1385 },
1386 base: baseEvent{
1387 typ: evSync,
1388 time: ts,
1389 },
1390 }
1391 ev.base.args[0] = uint64(n)
1392 return ev
1393 }
1394
View as plain text