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