1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file contains data types that all implementations of the trace format
6 // parser need to provide to the rest of the package.
14 "internal/trace/v2/event"
15 "internal/trace/v2/event/go122"
16 "internal/trace/v2/version"
19 // maxArgs is the maximum number of arguments for "plain" events,
20 // i.e. anything that could reasonably be represented as a Base.
23 // baseEvent is the basic unprocessed event. This serves as a common
24 // fundamental data structure across.
25 type baseEvent struct {
28 args [maxArgs - 1]uint64
31 // extra returns a slice representing extra available space in args
32 // that the parser can use to pass data up into Event.
33 func (e *baseEvent) extra(v version.Version) []uint64 {
36 return e.args[len(go122.Specs()[e.typ].Args)-1:]
38 panic(fmt.Sprintf("unsupported version: go 1.%d", v))
41 // evTable contains the per-generation data necessary to
42 // interpret an individual event.
45 strings dataTable[stringID, string]
46 stacks dataTable[stackID, stack]
48 // extraStrings are strings that get generated during
49 // parsing but haven't come directly from the trace, so
50 // they don't appear in strings.
52 extraStringIDs map[string]extraStringID
53 nextExtra extraStringID
56 // addExtraString adds an extra string to the evTable and returns
57 // a unique ID for the string in the table.
58 func (t *evTable) addExtraString(s string) extraStringID {
62 if t.extraStringIDs == nil {
63 t.extraStringIDs = make(map[string]extraStringID)
65 if id, ok := t.extraStringIDs[s]; ok {
70 t.extraStrings = append(t.extraStrings, s)
71 t.extraStringIDs[s] = id
75 // getExtraString returns the extra string for the provided ID.
76 // The ID must have been produced by addExtraString for this evTable.
77 func (t *evTable) getExtraString(id extraStringID) string {
81 return t.extraStrings[id-1]
84 // dataTable is a mapping from EIs to Es.
85 type dataTable[EI ~uint64, E any] struct {
91 // insert tries to add a mapping from id to s.
93 // Returns an error if a mapping for id already exists, regardless
94 // of whether or not s is the same in content. This should be used
95 // for validation during parsing.
96 func (d *dataTable[EI, E]) insert(id EI, data E) error {
98 d.sparse = make(map[EI]E)
100 if existing, ok := d.get(id); ok {
101 return fmt.Errorf("multiple %Ts with the same ID: id=%d, new=%v, existing=%v", data, id, data, existing)
107 // compactify attempts to compact sparse into dense.
109 // This is intended to be called only once after insertions are done.
110 func (d *dataTable[EI, E]) compactify() {
111 if d.sparse == nil || len(d.dense) != 0 {
112 // Already compactified.
115 // Find the range of IDs.
118 for id := range d.sparse {
126 // We're willing to waste at most 2x memory.
127 if int(maxID-minID) > 2*len(d.sparse) {
130 if int(minID) > len(d.sparse) {
133 size := int(maxID) + 1
134 d.present = make([]uint8, (size+7)/8)
135 d.dense = make([]E, size)
136 for id, data := range d.sparse {
138 d.present[id/8] |= uint8(1) << (id % 8)
143 // get returns the E for id or false if it doesn't
144 // exist. This should be used for validation during parsing.
145 func (d *dataTable[EI, E]) get(id EI) (E, bool) {
149 if int(id) < len(d.dense) {
150 if d.present[id/8]&(uint8(1)<<(id%8)) != 0 {
151 return d.dense[id], true
153 } else if d.sparse != nil {
154 if data, ok := d.sparse[id]; ok {
158 return *new(E), false
161 // forEach iterates over all ID/value pairs in the data table.
162 func (d *dataTable[EI, E]) forEach(yield func(EI, E) bool) bool {
163 for id, value := range d.dense {
164 if d.present[id/8]&(uint8(1)<<(id%8)) == 0 {
167 if !yield(EI(id), value) {
174 for id, value := range d.sparse {
175 if !yield(id, value) {
182 // mustGet returns the E for id or panics if it fails.
184 // This should only be used if id has already been validated.
185 func (d *dataTable[EI, E]) mustGet(id EI) E {
186 data, ok := d.get(id)
188 panic(fmt.Sprintf("expected id %d in %T table", id, data))
193 // frequency is nanoseconds per timestamp unit.
194 type frequency float64
196 // mul multiplies an unprocessed to produce a time in nanoseconds.
197 func (f frequency) mul(t timestamp) Time {
198 return Time(float64(t) * float64(f))
201 // stringID is an index into the string table for a generation.
204 // extraStringID is an index into the extra string table for a generation.
205 type extraStringID uint64
207 // stackID is an index into the stack table for a generation.
210 // cpuSample represents a CPU profiling sample captured by the trace.
211 type cpuSample struct {
217 // asEvent produces a complete Event from a cpuSample. It needs
218 // the evTable from the generation that created it.
220 // We don't just store it as an Event in generation to minimize
221 // the amount of pointer data floating around.
222 func (s cpuSample) asEvent(table *evTable) Event {
223 // TODO(mknyszek): This is go122-specific, but shouldn't be.
224 // Generalize this in the future.
229 typ: go122.EvCPUSample,
233 e.base.args[0] = uint64(s.stack)
237 // stack represents a goroutine stack sample.
242 func (s stack) String() string {
243 var sb strings.Builder
244 for _, frame := range s.frames {
245 fmt.Fprintf(&sb, "\t%#v\n", frame)
250 // frame represents a single stack frame.