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