|  | // Copyright 2018 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package traceparser | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "sort" | 
|  | ) | 
|  |  | 
|  | // convert raw events into Events | 
|  |  | 
|  | func (p *Parsed) createEvents(f func(string)) error { | 
|  | // multiple passes: | 
|  | // create some Events | 
|  | // sort them by time (and adjust their times to be nanonseconds) | 
|  | // remove events not in the desired time interval | 
|  | // make the events consistent (adding initializing events at the beginning) | 
|  | // remove the futile events | 
|  | // add links (and do final checking) | 
|  |  | 
|  | // shared by firstEvents | 
|  | p.byproc = make(map[int][]*Event) | 
|  | p.lastGs = make(map[int]uint64) | 
|  |  | 
|  | // p.batches are always sorted by time. otherwise a batch for one p that is totally | 
|  | // later than another batch might be done first, confusing us about g's | 
|  | for i, b := range p.batches { | 
|  | if b.raws == nil { | 
|  | continue | 
|  | } | 
|  | if err := p.firstEvents(b); err != nil { | 
|  | return fmt.Errorf("%v", err) // PJW: this is not useful | 
|  | } | 
|  | // we done with b.raws now | 
|  | p.batches[i].raws = nil | 
|  | } | 
|  | f("firstEvents finished") | 
|  | sorted := []*Event{} | 
|  | for _, v := range p.byproc { | 
|  | sorted = append(sorted, v...) | 
|  | } | 
|  | // PJW: are we done with p.byproc now? Yes. This shrinks a little. | 
|  | p.byproc = nil | 
|  | // Why wasn't this done earlier? Or, why do it at all? | 
|  | for _, ev := range sorted { | 
|  | switch ev.Type { | 
|  | case EvGoStartLocal: | 
|  | ev.Type = EvGoStart | 
|  | case EvGoUnblockLocal: | 
|  | ev.Type = EvGoUnblock | 
|  | case EvGoSysExitLocal: | 
|  | ev.Type = EvGoSysExit | 
|  | } | 
|  | } | 
|  | // change to nanoseconds | 
|  | freq := 1e9 / float64(p.TicksPerSec) | 
|  | for i, ev := range sorted { | 
|  | // Move timers and syscalls to separate fake Ps. | 
|  | // This could be done in the loop at line 38 | 
|  | // or maybe after robust fixes things. | 
|  | if p.timerGoids[ev.G] && ev.Type == EvGoUnblock { | 
|  | ev.Args[2] = uint64(ev.P) // save for robust() to use | 
|  | ev.P = TimerP | 
|  | } | 
|  | // sometimes the ts is not what it should be | 
|  | if ev.Type == EvGoSysExit { | 
|  | ev.P = SyscallP | 
|  | if ev.Args[2] != 0 { | 
|  | // PJW: test for this being safe. There might be no preceding | 
|  | // EvSysBlock, EvGoInSyscall, or its time might be later than this | 
|  | ev.Ts = int64(ev.Args[2]) | 
|  | } | 
|  | } | 
|  | if ev.Type == EvGCStart { | 
|  | ev.P = GCP | 
|  | } | 
|  | t := ev.Ts - p.minticks | 
|  | if t < 0 { | 
|  | return fmt.Errorf("event %d %s would be %d mints=%x", i, ev, t, p.minticks) | 
|  | } | 
|  | ev.Ts = int64(float64(ev.Ts-p.minticks) * freq) | 
|  | } | 
|  | // Stable for the case of equal Ts's. | 
|  | sort.SliceStable(sorted, func(i, j int) bool { return sorted[i].Ts < sorted[j].Ts }) | 
|  | f("sorted") | 
|  | // and ignore the ones with times out of bounds | 
|  | firstwant, lastwant := 0, len(sorted) | 
|  | for i, ev := range sorted { | 
|  | if ev.Ts < p.MinWant { | 
|  | firstwant = i + 1 | 
|  | } else if ev.Ts > p.MaxWant { // closed interval [minwant, maxwant] | 
|  | lastwant = i | 
|  | break // sorted by Ts | 
|  | } | 
|  | } | 
|  | f("nanoseconds") | 
|  | var err error | 
|  | sorted, _, err = p.robust(sorted[firstwant:lastwant]) // PJW: copy info from aux | 
|  | f("consistent") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | events, cnt := p.removeFutile(sorted) // err is always nil here. | 
|  | f(fmt.Sprintf("removed %d futiles", cnt)) | 
|  | // and finally, do some checks and put in links | 
|  | err = p.postProcess(events) | 
|  | f("post processed") | 
|  | if err != nil { | 
|  | return err // PJW: is this enough? NO | 
|  | } | 
|  | p.Events = events | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // Special P identifiers. | 
|  | const ( | 
|  | FakeP    = 1000000 + iota | 
|  | TimerP   // depicts timer unblocks | 
|  | NetpollP // depicts network unblocks | 
|  | SyscallP // depicts returns from syscalls | 
|  | GCP      // depicts GC state | 
|  | ) | 
|  |  | 
|  | // convert the raw events for a batch into Events, and keep track of | 
|  | // which G is running on the P that is common to the batch. | 
|  | func (p *Parsed) firstEvents(b batch) error { | 
|  | for _, raw := range b.raws { | 
|  | desc := EventDescriptions[raw.typ] | 
|  | narg := p.rawArgNum(&raw) | 
|  | if p.Err != nil { | 
|  | return p.Err | 
|  | } | 
|  | if raw.typ == EvBatch { | 
|  | // first event, record information about P, G, and Ts | 
|  | p.lastGs[p.lastP] = p.lastG // 0 the first time through | 
|  | p.lastP = int(raw.Arg(0)) | 
|  | p.lastG = p.lastGs[p.lastP] | 
|  | p.lastTs = int64(raw.Arg(1)) | 
|  | continue | 
|  | } | 
|  | e := &Event{Type: raw.typ, P: int32(p.lastP), G: p.lastG} | 
|  | var argoffset int | 
|  | if p.Version < 1007 { // can't happen. | 
|  | e.Ts = p.lastTs + int64(raw.Arg(1)) | 
|  | argoffset = 2 | 
|  | } else { | 
|  | e.Ts = p.lastTs + int64(raw.Arg(0)) | 
|  | argoffset = 1 | 
|  | } | 
|  | p.lastTs = e.Ts | 
|  | // collect the args for the raw event e | 
|  | for i := argoffset; i < narg; i++ { | 
|  | // evade one byte of corruption (from fuzzing typically) | 
|  | if raw.args == nil { | 
|  | return fmt.Errorf("raw.args is nil %s", evname(raw.typ)) | 
|  | } | 
|  | if i > 0 && i-1 >= len(*raw.args) { | 
|  | return fmt.Errorf("%s wants arg %d of %d", evname(raw.typ), i, len(*raw.args)) | 
|  | } | 
|  | if i == narg-1 && desc.Stack { | 
|  | e.StkID = uint32(raw.Arg(i)) | 
|  | } else { | 
|  | e.Args[i-argoffset] = raw.Arg(i) | 
|  | } | 
|  | } | 
|  | switch raw.typ { | 
|  | case EvGoSysCall, EvGCSweepDone, EvGCSweepStart: | 
|  | if e.G == 0 { | 
|  | // missing some earlier G's from this P | 
|  | continue // so we don't know which G is doing it | 
|  | } | 
|  | case EvGoStart, EvGoStartLocal, EvGoStartLabel: | 
|  | p.lastG = e.Args[0] | 
|  | e.G = p.lastG | 
|  | if raw.typ == EvGoStartLabel { | 
|  | e.SArgs = []string{p.Strings[e.Args[2]]} | 
|  | } | 
|  | case EvGCSTWStart: | 
|  | e.G = 0 | 
|  | switch e.Args[0] { | 
|  | case 0: | 
|  | e.SArgs = []string{"mark termination"} | 
|  | case 1: | 
|  | e.SArgs = []string{"sweep termination"} | 
|  | default: | 
|  | return fmt.Errorf("unknown STW kind %d!=0,1 %s", e.Args[0], e) | 
|  | } | 
|  | case EvGCStart, EvGCDone, EvGCSTWDone: | 
|  | e.G = 0 | 
|  | case EvGoEnd, EvGoStop, EvGoSched, EvGoPreempt, | 
|  | EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv, | 
|  | EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, | 
|  | EvGoSysBlock, EvGoBlockGC: | 
|  | p.lastG = 0 | 
|  | if e.G == 0 { | 
|  | // missing some earlier G's from this P | 
|  | continue // so we don't know which G is doing it | 
|  | } | 
|  | case EvGoSysExit, EvGoWaiting, EvGoInSyscall: | 
|  | e.G = e.Args[0] | 
|  | case EvUserTaskCreate: | 
|  | // e.Args 0: taskID, 1:parentID, 2:nameID | 
|  | e.SArgs = []string{p.Strings[e.Args[2]]} | 
|  | case EvUserRegion: | 
|  | if e.G == 0 { | 
|  | continue // don't know its G | 
|  | } | 
|  | // e.Args 0: taskID, 1: mode, 2:nameID | 
|  | e.SArgs = []string{p.Strings[e.Args[2]]} | 
|  | case EvUserLog: | 
|  | // e.Args 0: taskID, 1:keyID, 2: stackID | 
|  | e.SArgs = []string{p.Strings[e.Args[1]], raw.sarg} | 
|  | } | 
|  | p.byproc[p.lastP] = append(p.byproc[p.lastP], e) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (p *Parsed) removeFutile(events []*Event) ([]*Event, int) { | 
|  | // Phase 1: determine futile wakeup sequences. | 
|  | type G struct { | 
|  | futile bool | 
|  | wakeup []*Event // wakeup sequence (subject for removal) | 
|  | } | 
|  | gs := make(map[uint64]G) | 
|  | futile := make(map[*Event]bool) | 
|  | cnt := 0 | 
|  | for _, ev := range events { | 
|  | switch ev.Type { | 
|  | case EvGoUnblock: | 
|  | g := gs[ev.Args[0]] | 
|  | g.wakeup = []*Event{ev} | 
|  | gs[ev.Args[0]] = g | 
|  | case EvGoStart, EvGoPreempt, EvFutileWakeup: | 
|  | g := gs[ev.G] | 
|  | g.wakeup = append(g.wakeup, ev) | 
|  | if ev.Type == EvFutileWakeup { | 
|  | g.futile = true | 
|  | } | 
|  | gs[ev.G] = g | 
|  | case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, | 
|  | EvGoBlockSync, EvGoBlockCond: | 
|  | g := gs[ev.G] | 
|  | if g.futile { | 
|  | futile[ev] = true | 
|  | for _, ev1 := range g.wakeup { | 
|  | futile[ev1] = true | 
|  | } | 
|  | } | 
|  | delete(gs, ev.G) | 
|  | cnt++ | 
|  | } | 
|  | } | 
|  | // Phase 2: remove futile wakeup sequences. | 
|  | newEvents := events[:0] // overwrite the original slice | 
|  | for _, ev := range events { | 
|  | if !futile[ev] { | 
|  | newEvents = append(newEvents, ev) | 
|  | } | 
|  | } | 
|  | return newEvents, cnt // PJW: cnt doesn't count the futile[]s | 
|  | } | 
|  |  | 
|  | // Arg gets the n-th arg from a raw event | 
|  | func (r *rawEvent) Arg(n int) uint64 { | 
|  | if n == 0 { | 
|  | return r.arg0 | 
|  | } | 
|  | return (*r.args)[n-1] | 
|  | } | 
|  |  | 
|  | // report the number of arguments. (historical differences) | 
|  | func (p *Parsed) rawArgNum(ev *rawEvent) int { | 
|  | desc := EventDescriptions[ev.typ] | 
|  | switch ev.typ { | 
|  | case EvStack, EvFrequency, EvTimerGoroutine: | 
|  | p.Err = fmt.Errorf("%s unexpected in rawArgNum", evname(ev.typ)) | 
|  | return 0 | 
|  | } | 
|  | narg := len(desc.Args) | 
|  | if desc.Stack { | 
|  | narg++ | 
|  | } | 
|  | if ev.typ == EvBatch { | 
|  | if p.Version < 1007 { | 
|  | narg++ // used to be an extra unused arg | 
|  | } | 
|  | return narg | 
|  | } | 
|  | narg++ // timestamp | 
|  | if p.Version < 1007 { | 
|  | narg++ // sequence | 
|  | } | 
|  | // various special historical cases | 
|  | switch ev.typ { | 
|  | case EvGCSweepDone: | 
|  | if p.Version < 1009 { | 
|  | narg -= 2 // 1.9 added 2 args | 
|  | } | 
|  | case EvGCStart, EvGoStart, EvGoUnblock: | 
|  | if p.Version < 1007 { | 
|  | narg-- // one more since 1.7 | 
|  | } | 
|  | case EvGCSTWStart: | 
|  | if p.Version < 1010 { | 
|  | narg-- // 1.10 added an argument | 
|  | } | 
|  | } | 
|  | return narg | 
|  | } |