|
| 1 | +// Copyright 2018 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 | +package traceparser |
| 6 | + |
| 7 | +// postProcess is a final check of consistency, and if all is well, |
| 8 | +// adds links to Events |
| 9 | + |
| 10 | +import ( |
| 11 | + "fmt" |
| 12 | +) |
| 13 | + |
| 14 | +type gStatus int |
| 15 | + |
| 16 | +const ( |
| 17 | + gDead gStatus = iota |
| 18 | + gRunnable |
| 19 | + gRunning |
| 20 | + gWaiting |
| 21 | +) |
| 22 | + |
| 23 | +// This code is copied from internal/trace/parser.go. With greater understanding it could be |
| 24 | +// simplified. Sets ev.P for GCStart, and set various Link fields |
| 25 | +func (p *Parsed) postProcess(events []*Event) error { |
| 26 | + type gdesc struct { |
| 27 | + state gStatus |
| 28 | + ev *Event |
| 29 | + evStart *Event |
| 30 | + evCreate *Event |
| 31 | + evMarkAssist *Event |
| 32 | + } |
| 33 | + type pdesc struct { |
| 34 | + running bool |
| 35 | + g uint64 |
| 36 | + evSTW *Event |
| 37 | + evSweep *Event |
| 38 | + } |
| 39 | + |
| 40 | + gs := make(map[uint64]gdesc) |
| 41 | + ps := make(map[int]pdesc) |
| 42 | + tasks := make(map[uint64]*Event) // task id to task creation events |
| 43 | + activeRegions := make(map[uint64][]*Event) // goroutine id to stack of spans |
| 44 | + gs[0] = gdesc{state: gRunning} |
| 45 | + var evGC, evSTW *Event |
| 46 | + |
| 47 | + checkRunning := func(pd pdesc, g gdesc, ev *Event, allowG0 bool) error { |
| 48 | + if g.state != gRunning { |
| 49 | + return fmt.Errorf("saw %v, but g %d is not running", ev, ev.G) |
| 50 | + } |
| 51 | + if pd.g != ev.G { |
| 52 | + return fmt.Errorf("saw %v, but it's P is running %d, not %d", ev, pd.g, ev.G) |
| 53 | + } |
| 54 | + if !allowG0 && ev.G == 0 { |
| 55 | + return fmt.Errorf("saw %v with unexpected g==0", ev) |
| 56 | + } |
| 57 | + return nil |
| 58 | + } |
| 59 | + for i, ev := range events { |
| 60 | + g := gs[ev.G] |
| 61 | + px := ps[int(ev.P)] |
| 62 | + switch ev.Type { |
| 63 | + case EvProcStart: |
| 64 | + if px.running { |
| 65 | + return fmt.Errorf("%d: running before start %s", i, ev) |
| 66 | + } |
| 67 | + px.running = true |
| 68 | + case EvProcStop: |
| 69 | + if !px.running { |
| 70 | + return fmt.Errorf("%d: p %d not running %s", i, ev.P, ev) |
| 71 | + } |
| 72 | + if px.g != 0 { |
| 73 | + return fmt.Errorf("p %d is running a goroutine %s", ev.P, ev) |
| 74 | + } |
| 75 | + px.running = false |
| 76 | + case EvGCStart: |
| 77 | + if evGC != nil { |
| 78 | + return fmt.Errorf("GC already running %s, was %s", ev, evGC) |
| 79 | + } |
| 80 | + evGC = ev |
| 81 | + // Attribute this to the global GC state. |
| 82 | + ev.P = GCP |
| 83 | + case EvGCDone: |
| 84 | + if evGC == nil { |
| 85 | + return fmt.Errorf("%d:%s bogus GC end", i, ev) |
| 86 | + } |
| 87 | + evGC.Link = ev |
| 88 | + evGC = nil |
| 89 | + case EvGCSTWStart: |
| 90 | + evp := &evSTW |
| 91 | + if p.Version < 1010 { |
| 92 | + // Before 1.10, EvGCSTWStart was per-P. |
| 93 | + evp = &px.evSTW |
| 94 | + } |
| 95 | + if *evp != nil { |
| 96 | + return fmt.Errorf("STW %s still running at %s", *evp, ev) |
| 97 | + } |
| 98 | + *evp = ev |
| 99 | + case EvGCSTWDone: |
| 100 | + evp := &evSTW |
| 101 | + if p.Version < 1010 { |
| 102 | + // Before 1.10, EvGCSTWDone was per-P. |
| 103 | + evp = &px.evSTW |
| 104 | + } |
| 105 | + if *evp == nil { |
| 106 | + return fmt.Errorf("%d: no STW running %s", i, ev) |
| 107 | + } |
| 108 | + (*evp).Link = ev |
| 109 | + *evp = nil |
| 110 | + case EvGCMarkAssistStart: |
| 111 | + if g.evMarkAssist != nil { |
| 112 | + return fmt.Errorf("%d: MarkAssist %s is still running at %s", |
| 113 | + i, g.evMarkAssist, ev) |
| 114 | + } |
| 115 | + g.evMarkAssist = ev |
| 116 | + case EvGCMarkAssistDone: |
| 117 | + // Unlike most events, mark assists can be in progress when a |
| 118 | + // goroutine starts tracing, so we can't report an error here. |
| 119 | + if g.evMarkAssist != nil { |
| 120 | + g.evMarkAssist.Link = ev |
| 121 | + g.evMarkAssist = nil |
| 122 | + } |
| 123 | + case EvGCSweepStart: |
| 124 | + if px.evSweep != nil { |
| 125 | + return fmt.Errorf("sweep not done %d: %s", i, ev) |
| 126 | + } |
| 127 | + px.evSweep = ev |
| 128 | + case EvGCSweepDone: |
| 129 | + if px.evSweep == nil { |
| 130 | + return fmt.Errorf("%d: no sweep happening %s", i, ev) |
| 131 | + } |
| 132 | + px.evSweep.Link = ev |
| 133 | + px.evSweep = nil |
| 134 | + case EvGoWaiting: |
| 135 | + if g.state != gRunnable { |
| 136 | + return fmt.Errorf("not runnable before %d:%s", i, ev) |
| 137 | + } |
| 138 | + g.state = gWaiting |
| 139 | + g.ev = ev |
| 140 | + case EvGoInSyscall: |
| 141 | + if g.state != gRunnable { |
| 142 | + return fmt.Errorf("not runnable before %d:%s", i, ev) |
| 143 | + } |
| 144 | + g.state = gWaiting |
| 145 | + g.ev = ev |
| 146 | + case EvGoCreate: |
| 147 | + if err := checkRunning(px, g, ev, true); err != nil { |
| 148 | + return err |
| 149 | + } |
| 150 | + if _, ok := gs[ev.Args[0]]; ok { |
| 151 | + return fmt.Errorf("%d: already exists %s", i, ev) |
| 152 | + } |
| 153 | + gs[ev.Args[0]] = gdesc{state: gRunnable, ev: ev, evCreate: ev} |
| 154 | + case EvGoStart, EvGoStartLabel: |
| 155 | + if g.state != gRunnable { |
| 156 | + return fmt.Errorf("not runnable before start %d:%s %+v", i, ev, g) |
| 157 | + } |
| 158 | + if px.g != 0 { |
| 159 | + return fmt.Errorf("%d: %s has p running %d already %v", i, ev, px.g, px) |
| 160 | + } |
| 161 | + g.state = gRunning |
| 162 | + g.evStart = ev |
| 163 | + px.g = ev.G |
| 164 | + if g.evCreate != nil { |
| 165 | + if p.Version < 1007 { |
| 166 | + // +1 because symbolizer expects return pc. |
| 167 | + //PJW: aren't doing < 1007. ev.stk = []*Frame{{PC: g.evCreate.args[1] + 1}} |
| 168 | + } else { |
| 169 | + ev.StkID = uint32(g.evCreate.Args[1]) |
| 170 | + } |
| 171 | + g.evCreate = nil |
| 172 | + } |
| 173 | + |
| 174 | + if g.ev != nil { |
| 175 | + g.ev.Link = ev |
| 176 | + g.ev = nil |
| 177 | + } |
| 178 | + case EvGoEnd, EvGoStop: |
| 179 | + if err := checkRunning(px, g, ev, false); err != nil { |
| 180 | + return fmt.Errorf("%d: %s", i, err) |
| 181 | + } |
| 182 | + g.evStart.Link = ev |
| 183 | + g.evStart = nil |
| 184 | + g.state = gDead |
| 185 | + px.g = 0 |
| 186 | + |
| 187 | + if ev.Type == EvGoEnd { // flush all active Regions |
| 188 | + spans := activeRegions[ev.G] |
| 189 | + for _, s := range spans { |
| 190 | + s.Link = ev |
| 191 | + } |
| 192 | + delete(activeRegions, ev.G) |
| 193 | + } |
| 194 | + case EvGoSched, EvGoPreempt: |
| 195 | + if err := checkRunning(px, g, ev, false); err != nil { |
| 196 | + return err |
| 197 | + } |
| 198 | + g.state = gRunnable |
| 199 | + g.evStart.Link = ev |
| 200 | + g.evStart = nil |
| 201 | + px.g = 0 |
| 202 | + g.ev = ev |
| 203 | + case EvGoUnblock: |
| 204 | + if g.state != gRunning { // PJW, error message |
| 205 | + return fmt.Errorf("Event %d (%s) is not running at unblock %s", i, ev, g.state) |
| 206 | + |
| 207 | + } |
| 208 | + if ev.P != TimerP && px.g != ev.G { |
| 209 | + // PJW: do better here. |
| 210 | + return fmt.Errorf("%d: %s p %d is not running g", i, ev, px.g) |
| 211 | + } |
| 212 | + g1 := gs[ev.Args[0]] |
| 213 | + if g1.state != gWaiting { |
| 214 | + return fmt.Errorf("g %v is not waiting before unpark i=%d g1=%v %s", |
| 215 | + ev.Args[0], i, g1, ev) |
| 216 | + } |
| 217 | + if g1.ev != nil && g1.ev.Type == EvGoBlockNet && ev.P != TimerP { |
| 218 | + ev.P = NetpollP |
| 219 | + } |
| 220 | + if g1.ev != nil { |
| 221 | + g1.ev.Link = ev |
| 222 | + } |
| 223 | + g1.state = gRunnable |
| 224 | + g1.ev = ev |
| 225 | + gs[ev.Args[0]] = g1 |
| 226 | + case EvGoSysCall: |
| 227 | + if err := checkRunning(px, g, ev, false); err != nil { |
| 228 | + return err |
| 229 | + } |
| 230 | + g.ev = ev |
| 231 | + case EvGoSysBlock: |
| 232 | + if err := checkRunning(px, g, ev, false); err != nil { |
| 233 | + return err |
| 234 | + } |
| 235 | + g.state = gWaiting |
| 236 | + g.evStart.Link = ev |
| 237 | + g.evStart = nil |
| 238 | + px.g = 0 |
| 239 | + case EvGoSysExit: |
| 240 | + if g.state != gWaiting { |
| 241 | + return fmt.Errorf("not waiting when %s", ev) |
| 242 | + } |
| 243 | + if g.ev != nil && g.ev.Type == EvGoSysCall { |
| 244 | + g.ev.Link = ev |
| 245 | + } |
| 246 | + g.state = gRunnable |
| 247 | + g.ev = ev |
| 248 | + case EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv, |
| 249 | + EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoBlockGC: |
| 250 | + if err := checkRunning(px, g, ev, false); err != nil { |
| 251 | + return err |
| 252 | + } |
| 253 | + g.state = gWaiting |
| 254 | + g.ev = ev |
| 255 | + g.evStart.Link = ev |
| 256 | + g.evStart = nil |
| 257 | + px.g = 0 |
| 258 | + case EvUserTaskCreate: |
| 259 | + taskid := ev.Args[0] |
| 260 | + if prevEv, ok := tasks[taskid]; ok { |
| 261 | + return fmt.Errorf("task id conflicts (id:%d), %q vs %q", taskid, ev, prevEv) |
| 262 | + } |
| 263 | + tasks[ev.Args[0]] = ev |
| 264 | + case EvUserTaskEnd: |
| 265 | + if prevEv, ok := tasks[ev.Args[0]]; ok { |
| 266 | + prevEv.Link = ev |
| 267 | + ev.Link = prevEv |
| 268 | + } |
| 269 | + case EvUserRegion: |
| 270 | + mode := ev.Args[1] |
| 271 | + spans := activeRegions[ev.G] |
| 272 | + if mode == 0 { // span start |
| 273 | + activeRegions[ev.G] = append(spans, ev) // push |
| 274 | + } else if mode == 1 { // span end |
| 275 | + n := len(spans) |
| 276 | + if n > 0 { // matching span start event is in the trace. |
| 277 | + s := spans[n-1] |
| 278 | + if s.Args[0] != ev.Args[0] || s.SArgs[0] != ev.SArgs[0] { // task id, span name mismatch |
| 279 | + return fmt.Errorf("misuse of span in goroutine %d: span end %q when the inner-most active span start event is %q", |
| 280 | + ev.G, ev, s) |
| 281 | + } |
| 282 | + // Link span start event with span end event |
| 283 | + s.Link = ev |
| 284 | + ev.Link = s |
| 285 | + |
| 286 | + if n > 1 { |
| 287 | + activeRegions[ev.G] = spans[:n-1] |
| 288 | + } else { |
| 289 | + delete(activeRegions, ev.G) |
| 290 | + } |
| 291 | + } |
| 292 | + } else { |
| 293 | + return fmt.Errorf("invalid user region, mode: %q", ev) |
| 294 | + } |
| 295 | + } |
| 296 | + gs[ev.G] = g |
| 297 | + ps[int(ev.P)] = px |
| 298 | + } |
| 299 | + return nil |
| 300 | +} |
| 301 | +func (g gStatus) String() string { |
| 302 | + switch g { |
| 303 | + case gDead: |
| 304 | + return "gDead" |
| 305 | + case gRunnable: |
| 306 | + return "gRunnable" |
| 307 | + case gRunning: |
| 308 | + return "gRunning" |
| 309 | + case gWaiting: |
| 310 | + return "gWaiting" |
| 311 | + } |
| 312 | + return fmt.Sprintf("gStatus?%d", g) |
| 313 | +} |
0 commit comments