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