golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/trace/base.go (about) 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. 4 5 // Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT. 6 7 //go:build go1.21 8 9 // This file contains data types that all implementations of the trace format 10 // parser need to provide to the rest of the package. 11 12 package trace 13 14 import ( 15 "fmt" 16 "math" 17 "strings" 18 19 "golang.org/x/exp/trace/internal/event" 20 "golang.org/x/exp/trace/internal/event/go122" 21 "golang.org/x/exp/trace/internal/version" 22 ) 23 24 // maxArgs is the maximum number of arguments for "plain" events, 25 // i.e. anything that could reasonably be represented as a baseEvent. 26 // 27 // TODO(mknyszek): This is only 6 instead of 5 because GoStatusStack 28 // has 5 arguments and needs to smuggle in a 6th. Figure out a way to 29 // shrink this in the future. 30 const maxArgs = 6 31 32 // timedEventArgs is an array that is able to hold the arguments for any 33 // timed event. 34 type timedEventArgs [maxArgs - 1]uint64 35 36 // baseEvent is the basic unprocessed event. This serves as a common 37 // fundamental data structure across. 38 type baseEvent struct { 39 typ event.Type 40 time Time 41 args timedEventArgs 42 } 43 44 // extra returns a slice representing extra available space in args 45 // that the parser can use to pass data up into Event. 46 func (e *baseEvent) extra(v version.Version) []uint64 { 47 switch v { 48 case version.Go122: 49 return e.args[len(go122.Specs()[e.typ].Args)-1:] 50 } 51 panic(fmt.Sprintf("unsupported version: go 1.%d", v)) 52 } 53 54 // evTable contains the per-generation data necessary to 55 // interpret an individual event. 56 type evTable struct { 57 freq frequency 58 strings dataTable[stringID, string] 59 stacks dataTable[stackID, stack] 60 pcs map[uint64]frame 61 62 // extraStrings are strings that get generated during 63 // parsing but haven't come directly from the trace, so 64 // they don't appear in strings. 65 extraStrings []string 66 extraStringIDs map[string]extraStringID 67 nextExtra extraStringID 68 } 69 70 // addExtraString adds an extra string to the evTable and returns 71 // a unique ID for the string in the table. 72 func (t *evTable) addExtraString(s string) extraStringID { 73 if s == "" { 74 return 0 75 } 76 if t.extraStringIDs == nil { 77 t.extraStringIDs = make(map[string]extraStringID) 78 } 79 if id, ok := t.extraStringIDs[s]; ok { 80 return id 81 } 82 t.nextExtra++ 83 id := t.nextExtra 84 t.extraStrings = append(t.extraStrings, s) 85 t.extraStringIDs[s] = id 86 return id 87 } 88 89 // getExtraString returns the extra string for the provided ID. 90 // The ID must have been produced by addExtraString for this evTable. 91 func (t *evTable) getExtraString(id extraStringID) string { 92 if id == 0 { 93 return "" 94 } 95 return t.extraStrings[id-1] 96 } 97 98 // dataTable is a mapping from EIs to Es. 99 type dataTable[EI ~uint64, E any] struct { 100 present []uint8 101 dense []E 102 sparse map[EI]E 103 } 104 105 // insert tries to add a mapping from id to s. 106 // 107 // Returns an error if a mapping for id already exists, regardless 108 // of whether or not s is the same in content. This should be used 109 // for validation during parsing. 110 func (d *dataTable[EI, E]) insert(id EI, data E) error { 111 if d.sparse == nil { 112 d.sparse = make(map[EI]E) 113 } 114 if existing, ok := d.get(id); ok { 115 return fmt.Errorf("multiple %Ts with the same ID: id=%d, new=%v, existing=%v", data, id, data, existing) 116 } 117 d.sparse[id] = data 118 return nil 119 } 120 121 // compactify attempts to compact sparse into dense. 122 // 123 // This is intended to be called only once after insertions are done. 124 func (d *dataTable[EI, E]) compactify() { 125 if d.sparse == nil || len(d.dense) != 0 { 126 // Already compactified. 127 return 128 } 129 // Find the range of IDs. 130 maxID := EI(0) 131 minID := ^EI(0) 132 for id := range d.sparse { 133 if id > maxID { 134 maxID = id 135 } 136 if id < minID { 137 minID = id 138 } 139 } 140 if maxID >= math.MaxInt { 141 // We can't create a slice big enough to hold maxID elements 142 return 143 } 144 // We're willing to waste at most 2x memory. 145 if int(maxID-minID) > max(len(d.sparse), 2*len(d.sparse)) { 146 return 147 } 148 if int(minID) > len(d.sparse) { 149 return 150 } 151 size := int(maxID) + 1 152 d.present = make([]uint8, (size+7)/8) 153 d.dense = make([]E, size) 154 for id, data := range d.sparse { 155 d.dense[id] = data 156 d.present[id/8] |= uint8(1) << (id % 8) 157 } 158 d.sparse = nil 159 } 160 161 // get returns the E for id or false if it doesn't 162 // exist. This should be used for validation during parsing. 163 func (d *dataTable[EI, E]) get(id EI) (E, bool) { 164 if id == 0 { 165 return *new(E), true 166 } 167 if uint64(id) < uint64(len(d.dense)) { 168 if d.present[id/8]&(uint8(1)<<(id%8)) != 0 { 169 return d.dense[id], true 170 } 171 } else if d.sparse != nil { 172 if data, ok := d.sparse[id]; ok { 173 return data, true 174 } 175 } 176 return *new(E), false 177 } 178 179 // forEach iterates over all ID/value pairs in the data table. 180 func (d *dataTable[EI, E]) forEach(yield func(EI, E) bool) bool { 181 for id, value := range d.dense { 182 if d.present[id/8]&(uint8(1)<<(id%8)) == 0 { 183 continue 184 } 185 if !yield(EI(id), value) { 186 return false 187 } 188 } 189 if d.sparse == nil { 190 return true 191 } 192 for id, value := range d.sparse { 193 if !yield(id, value) { 194 return false 195 } 196 } 197 return true 198 } 199 200 // mustGet returns the E for id or panics if it fails. 201 // 202 // This should only be used if id has already been validated. 203 func (d *dataTable[EI, E]) mustGet(id EI) E { 204 data, ok := d.get(id) 205 if !ok { 206 panic(fmt.Sprintf("expected id %d in %T table", id, data)) 207 } 208 return data 209 } 210 211 // frequency is nanoseconds per timestamp unit. 212 type frequency float64 213 214 // mul multiplies an unprocessed to produce a time in nanoseconds. 215 func (f frequency) mul(t timestamp) Time { 216 return Time(float64(t) * float64(f)) 217 } 218 219 // stringID is an index into the string table for a generation. 220 type stringID uint64 221 222 // extraStringID is an index into the extra string table for a generation. 223 type extraStringID uint64 224 225 // stackID is an index into the stack table for a generation. 226 type stackID uint64 227 228 // cpuSample represents a CPU profiling sample captured by the trace. 229 type cpuSample struct { 230 schedCtx 231 time Time 232 stack stackID 233 } 234 235 // asEvent produces a complete Event from a cpuSample. It needs 236 // the evTable from the generation that created it. 237 // 238 // We don't just store it as an Event in generation to minimize 239 // the amount of pointer data floating around. 240 func (s cpuSample) asEvent(table *evTable) Event { 241 // TODO(mknyszek): This is go122-specific, but shouldn't be. 242 // Generalize this in the future. 243 e := Event{ 244 table: table, 245 ctx: s.schedCtx, 246 base: baseEvent{ 247 typ: go122.EvCPUSample, 248 time: s.time, 249 }, 250 } 251 e.base.args[0] = uint64(s.stack) 252 return e 253 } 254 255 // stack represents a goroutine stack sample. 256 type stack struct { 257 pcs []uint64 258 } 259 260 func (s stack) String() string { 261 var sb strings.Builder 262 for _, frame := range s.pcs { 263 fmt.Fprintf(&sb, "\t%#v\n", frame) 264 } 265 return sb.String() 266 } 267 268 // frame represents a single stack frame. 269 type frame struct { 270 pc uint64 271 funcID stringID 272 fileID stringID 273 line uint64 274 }