|  | // Copyright 2016 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 trace | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "sort" | 
|  | ) | 
|  |  | 
|  | type eventBatch struct { | 
|  | events   []*Event | 
|  | selected bool | 
|  | } | 
|  |  | 
|  | type orderEvent struct { | 
|  | ev    *Event | 
|  | batch int | 
|  | g     uint64 | 
|  | init  gState | 
|  | next  gState | 
|  | } | 
|  |  | 
|  | type gStatus int | 
|  |  | 
|  | type gState struct { | 
|  | seq    uint64 | 
|  | status gStatus | 
|  | } | 
|  |  | 
|  | const ( | 
|  | gDead gStatus = iota | 
|  | gRunnable | 
|  | gRunning | 
|  | gWaiting | 
|  |  | 
|  | unordered = ^uint64(0) | 
|  | garbage   = ^uint64(0) - 1 | 
|  | noseq     = ^uint64(0) | 
|  | seqinc    = ^uint64(0) - 1 | 
|  | ) | 
|  |  | 
|  | // order1007 merges a set of per-P event batches into a single, consistent stream. | 
|  | // The high level idea is as follows. Events within an individual batch are in | 
|  | // correct order, because they are emitted by a single P. So we need to produce | 
|  | // a correct interleaving of the batches. To do this we take first unmerged event | 
|  | // from each batch (frontier). Then choose subset that is "ready" to be merged, | 
|  | // that is, events for which all dependencies are already merged. Then we choose | 
|  | // event with the lowest timestamp from the subset, merge it and repeat. | 
|  | // This approach ensures that we form a consistent stream even if timestamps are | 
|  | // incorrect (condition observed on some machines). | 
|  | func order1007(m map[int][]*Event) (events []*Event, err error) { | 
|  | pending := 0 | 
|  | var batches []*eventBatch | 
|  | for _, v := range m { | 
|  | pending += len(v) | 
|  | batches = append(batches, &eventBatch{v, false}) | 
|  | } | 
|  | gs := make(map[uint64]gState) | 
|  | var frontier []orderEvent | 
|  | for ; pending != 0; pending-- { | 
|  | for i, b := range batches { | 
|  | if b.selected || len(b.events) == 0 { | 
|  | continue | 
|  | } | 
|  | ev := b.events[0] | 
|  | g, init, next := stateTransition(ev) | 
|  | if !transitionReady(g, gs[g], init) { | 
|  | continue | 
|  | } | 
|  | frontier = append(frontier, orderEvent{ev, i, g, init, next}) | 
|  | b.events = b.events[1:] | 
|  | b.selected = true | 
|  | // Get rid of "Local" events, they are intended merely for ordering. | 
|  | switch ev.Type { | 
|  | case EvGoStartLocal: | 
|  | ev.Type = EvGoStart | 
|  | case EvGoUnblockLocal: | 
|  | ev.Type = EvGoUnblock | 
|  | case EvGoSysExitLocal: | 
|  | ev.Type = EvGoSysExit | 
|  | } | 
|  | } | 
|  | if len(frontier) == 0 { | 
|  | return nil, fmt.Errorf("no consistent ordering of events possible") | 
|  | } | 
|  | sort.Sort(orderEventList(frontier)) | 
|  | f := frontier[0] | 
|  | frontier[0] = frontier[len(frontier)-1] | 
|  | frontier = frontier[:len(frontier)-1] | 
|  | events = append(events, f.ev) | 
|  | transition(gs, f.g, f.init, f.next) | 
|  | if !batches[f.batch].selected { | 
|  | panic("frontier batch is not selected") | 
|  | } | 
|  | batches[f.batch].selected = false | 
|  | } | 
|  |  | 
|  | // At this point we have a consistent stream of events. | 
|  | // Make sure time stamps respect the ordering. | 
|  | // The tests will skip (not fail) the test case if they see this error. | 
|  | if !sort.IsSorted(eventList(events)) { | 
|  | return nil, ErrTimeOrder | 
|  | } | 
|  |  | 
|  | // The last part is giving correct timestamps to EvGoSysExit events. | 
|  | // The problem with EvGoSysExit is that actual syscall exit timestamp (ev.Args[2]) | 
|  | // is potentially acquired long before event emission. So far we've used | 
|  | // timestamp of event emission (ev.Ts). | 
|  | // We could not set ev.Ts = ev.Args[2] earlier, because it would produce | 
|  | // seemingly broken timestamps (misplaced event). | 
|  | // We also can't simply update the timestamp and resort events, because | 
|  | // if timestamps are broken we will misplace the event and later report | 
|  | // logically broken trace (instead of reporting broken timestamps). | 
|  | lastSysBlock := make(map[uint64]int64) | 
|  | for _, ev := range events { | 
|  | switch ev.Type { | 
|  | case EvGoSysBlock, EvGoInSyscall: | 
|  | lastSysBlock[ev.G] = ev.Ts | 
|  | case EvGoSysExit: | 
|  | ts := int64(ev.Args[2]) | 
|  | if ts == 0 { | 
|  | continue | 
|  | } | 
|  | block := lastSysBlock[ev.G] | 
|  | if block == 0 { | 
|  | return nil, fmt.Errorf("stray syscall exit") | 
|  | } | 
|  | if ts < block { | 
|  | return nil, ErrTimeOrder | 
|  | } | 
|  | ev.Ts = ts | 
|  | } | 
|  | } | 
|  | sort.Stable(eventList(events)) | 
|  |  | 
|  | return | 
|  | } | 
|  |  | 
|  | // stateTransition returns goroutine state (sequence and status) when the event | 
|  | // becomes ready for merging (init) and the goroutine state after the event (next). | 
|  | func stateTransition(ev *Event) (g uint64, init, next gState) { | 
|  | switch ev.Type { | 
|  | case EvGoCreate: | 
|  | g = ev.Args[0] | 
|  | init = gState{0, gDead} | 
|  | next = gState{1, gRunnable} | 
|  | case EvGoWaiting, EvGoInSyscall: | 
|  | g = ev.G | 
|  | init = gState{1, gRunnable} | 
|  | next = gState{2, gWaiting} | 
|  | case EvGoStart, EvGoStartLabel: | 
|  | g = ev.G | 
|  | init = gState{ev.Args[1], gRunnable} | 
|  | next = gState{ev.Args[1] + 1, gRunning} | 
|  | case EvGoStartLocal: | 
|  | // noseq means that this event is ready for merging as soon as | 
|  | // frontier reaches it (EvGoStartLocal is emitted on the same P | 
|  | // as the corresponding EvGoCreate/EvGoUnblock, and thus the latter | 
|  | // is already merged). | 
|  | // seqinc is a stub for cases when event increments g sequence, | 
|  | // but since we don't know current seq we also don't know next seq. | 
|  | g = ev.G | 
|  | init = gState{noseq, gRunnable} | 
|  | next = gState{seqinc, gRunning} | 
|  | case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, | 
|  | EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep, | 
|  | EvGoSysBlock, EvGoBlockGC: | 
|  | g = ev.G | 
|  | init = gState{noseq, gRunning} | 
|  | next = gState{noseq, gWaiting} | 
|  | case EvGoSched, EvGoPreempt: | 
|  | g = ev.G | 
|  | init = gState{noseq, gRunning} | 
|  | next = gState{noseq, gRunnable} | 
|  | case EvGoUnblock, EvGoSysExit: | 
|  | g = ev.Args[0] | 
|  | init = gState{ev.Args[1], gWaiting} | 
|  | next = gState{ev.Args[1] + 1, gRunnable} | 
|  | case EvGoUnblockLocal, EvGoSysExitLocal: | 
|  | g = ev.Args[0] | 
|  | init = gState{noseq, gWaiting} | 
|  | next = gState{seqinc, gRunnable} | 
|  | case EvGCStart: | 
|  | g = garbage | 
|  | init = gState{ev.Args[0], gDead} | 
|  | next = gState{ev.Args[0] + 1, gDead} | 
|  | default: | 
|  | // no ordering requirements | 
|  | g = unordered | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | func transitionReady(g uint64, curr, init gState) bool { | 
|  | return g == unordered || (init.seq == noseq || init.seq == curr.seq) && init.status == curr.status | 
|  | } | 
|  |  | 
|  | func transition(gs map[uint64]gState, g uint64, init, next gState) { | 
|  | if g == unordered { | 
|  | return | 
|  | } | 
|  | curr := gs[g] | 
|  | if !transitionReady(g, curr, init) { | 
|  | panic("event sequences are broken") | 
|  | } | 
|  | switch next.seq { | 
|  | case noseq: | 
|  | next.seq = curr.seq | 
|  | case seqinc: | 
|  | next.seq = curr.seq + 1 | 
|  | } | 
|  | gs[g] = next | 
|  | } | 
|  |  | 
|  | // order1005 merges a set of per-P event batches into a single, consistent stream. | 
|  | func order1005(m map[int][]*Event) (events []*Event, err error) { | 
|  | for _, batch := range m { | 
|  | events = append(events, batch...) | 
|  | } | 
|  | for _, ev := range events { | 
|  | if ev.Type == EvGoSysExit { | 
|  | // EvGoSysExit emission is delayed until the thread has a P. | 
|  | // Give it the real sequence number and time stamp. | 
|  | ev.seq = int64(ev.Args[1]) | 
|  | if ev.Args[2] != 0 { | 
|  | ev.Ts = int64(ev.Args[2]) | 
|  | } | 
|  | } | 
|  | } | 
|  | sort.Sort(eventSeqList(events)) | 
|  | if !sort.IsSorted(eventList(events)) { | 
|  | return nil, ErrTimeOrder | 
|  | } | 
|  | return | 
|  | } | 
|  |  | 
|  | type orderEventList []orderEvent | 
|  |  | 
|  | func (l orderEventList) Len() int { | 
|  | return len(l) | 
|  | } | 
|  |  | 
|  | func (l orderEventList) Less(i, j int) bool { | 
|  | return l[i].ev.Ts < l[j].ev.Ts | 
|  | } | 
|  |  | 
|  | func (l orderEventList) Swap(i, j int) { | 
|  | l[i], l[j] = l[j], l[i] | 
|  | } | 
|  |  | 
|  | type eventList []*Event | 
|  |  | 
|  | func (l eventList) Len() int { | 
|  | return len(l) | 
|  | } | 
|  |  | 
|  | func (l eventList) Less(i, j int) bool { | 
|  | return l[i].Ts < l[j].Ts | 
|  | } | 
|  |  | 
|  | func (l eventList) Swap(i, j int) { | 
|  | l[i], l[j] = l[j], l[i] | 
|  | } | 
|  |  | 
|  | type eventSeqList []*Event | 
|  |  | 
|  | func (l eventSeqList) Len() int { | 
|  | return len(l) | 
|  | } | 
|  |  | 
|  | func (l eventSeqList) Less(i, j int) bool { | 
|  | return l[i].seq < l[j].seq | 
|  | } | 
|  |  | 
|  | func (l eventSeqList) Swap(i, j int) { | 
|  | l[i], l[j] = l[j], l[i] | 
|  | } |