github.com/bir3/gocompiler@v0.9.2202/src/internal/trace/order.go (about)

     1  // Copyright 2016 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 trace
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  )
    11  
    12  type eventBatch struct {
    13  	events   []*Event
    14  	selected bool
    15  }
    16  
    17  type orderEvent struct {
    18  	ev    *Event
    19  	batch int
    20  	g     uint64
    21  	init  gState
    22  	next  gState
    23  }
    24  
    25  type gStatus int
    26  
    27  type gState struct {
    28  	seq    uint64
    29  	status gStatus
    30  }
    31  
    32  const (
    33  	gDead gStatus = iota
    34  	gRunnable
    35  	gRunning
    36  	gWaiting
    37  
    38  	unordered = ^uint64(0)
    39  	garbage   = ^uint64(0) - 1
    40  	noseq     = ^uint64(0)
    41  	seqinc    = ^uint64(0) - 1
    42  )
    43  
    44  // order1007 merges a set of per-P event batches into a single, consistent stream.
    45  // The high level idea is as follows. Events within an individual batch are in
    46  // correct order, because they are emitted by a single P. So we need to produce
    47  // a correct interleaving of the batches. To do this we take first unmerged event
    48  // from each batch (frontier). Then choose subset that is "ready" to be merged,
    49  // that is, events for which all dependencies are already merged. Then we choose
    50  // event with the lowest timestamp from the subset, merge it and repeat.
    51  // This approach ensures that we form a consistent stream even if timestamps are
    52  // incorrect (condition observed on some machines).
    53  func order1007(m map[int][]*Event) (events []*Event, err error) {
    54  	pending := 0
    55  	// The ordering of CPU profile sample events in the data stream is based on
    56  	// when each run of the signal handler was able to acquire the spinlock,
    57  	// with original timestamps corresponding to when ReadTrace pulled the data
    58  	// off of the profBuf queue. Re-sort them by the timestamp we captured
    59  	// inside the signal handler.
    60  	sort.Stable(eventList(m[ProfileP]))
    61  	var batches []*eventBatch
    62  	for _, v := range m {
    63  		pending += len(v)
    64  		batches = append(batches, &eventBatch{v, false})
    65  	}
    66  	gs := make(map[uint64]gState)
    67  	var frontier []orderEvent
    68  	for ; pending != 0; pending-- {
    69  		for i, b := range batches {
    70  			if b.selected || len(b.events) == 0 {
    71  				continue
    72  			}
    73  			ev := b.events[0]
    74  			g, init, next := stateTransition(ev)
    75  			if !transitionReady(g, gs[g], init) {
    76  				continue
    77  			}
    78  			frontier = append(frontier, orderEvent{ev, i, g, init, next})
    79  			b.events = b.events[1:]
    80  			b.selected = true
    81  			// Get rid of "Local" events, they are intended merely for ordering.
    82  			switch ev.Type {
    83  			case EvGoStartLocal:
    84  				ev.Type = EvGoStart
    85  			case EvGoUnblockLocal:
    86  				ev.Type = EvGoUnblock
    87  			case EvGoSysExitLocal:
    88  				ev.Type = EvGoSysExit
    89  			}
    90  		}
    91  		if len(frontier) == 0 {
    92  			return nil, fmt.Errorf("no consistent ordering of events possible")
    93  		}
    94  		sort.Sort(orderEventList(frontier))
    95  		f := frontier[0]
    96  		frontier[0] = frontier[len(frontier)-1]
    97  		frontier = frontier[:len(frontier)-1]
    98  		events = append(events, f.ev)
    99  		transition(gs, f.g, f.init, f.next)
   100  		if !batches[f.batch].selected {
   101  			panic("frontier batch is not selected")
   102  		}
   103  		batches[f.batch].selected = false
   104  	}
   105  
   106  	// At this point we have a consistent stream of events.
   107  	// Make sure time stamps respect the ordering.
   108  	// The tests will skip (not fail) the test case if they see this error.
   109  	if !sort.IsSorted(eventList(events)) {
   110  		return nil, ErrTimeOrder
   111  	}
   112  
   113  	// The last part is giving correct timestamps to EvGoSysExit events.
   114  	// The problem with EvGoSysExit is that actual syscall exit timestamp (ev.Args[2])
   115  	// is potentially acquired long before event emission. So far we've used
   116  	// timestamp of event emission (ev.Ts).
   117  	// We could not set ev.Ts = ev.Args[2] earlier, because it would produce
   118  	// seemingly broken timestamps (misplaced event).
   119  	// We also can't simply update the timestamp and resort events, because
   120  	// if timestamps are broken we will misplace the event and later report
   121  	// logically broken trace (instead of reporting broken timestamps).
   122  	lastSysBlock := make(map[uint64]int64)
   123  	for _, ev := range events {
   124  		switch ev.Type {
   125  		case EvGoSysBlock, EvGoInSyscall:
   126  			lastSysBlock[ev.G] = ev.Ts
   127  		case EvGoSysExit:
   128  			ts := int64(ev.Args[2])
   129  			if ts == 0 {
   130  				continue
   131  			}
   132  			block := lastSysBlock[ev.G]
   133  			if block == 0 {
   134  				return nil, fmt.Errorf("stray syscall exit")
   135  			}
   136  			if ts < block {
   137  				return nil, ErrTimeOrder
   138  			}
   139  			ev.Ts = ts
   140  		}
   141  	}
   142  	sort.Stable(eventList(events))
   143  
   144  	return
   145  }
   146  
   147  // stateTransition returns goroutine state (sequence and status) when the event
   148  // becomes ready for merging (init) and the goroutine state after the event (next).
   149  func stateTransition(ev *Event) (g uint64, init, next gState) {
   150  	switch ev.Type {
   151  	case EvGoCreate:
   152  		g = ev.Args[0]
   153  		init = gState{0, gDead}
   154  		next = gState{1, gRunnable}
   155  	case EvGoWaiting, EvGoInSyscall:
   156  		g = ev.G
   157  		init = gState{1, gRunnable}
   158  		next = gState{2, gWaiting}
   159  	case EvGoStart, EvGoStartLabel:
   160  		g = ev.G
   161  		init = gState{ev.Args[1], gRunnable}
   162  		next = gState{ev.Args[1] + 1, gRunning}
   163  	case EvGoStartLocal:
   164  		// noseq means that this event is ready for merging as soon as
   165  		// frontier reaches it (EvGoStartLocal is emitted on the same P
   166  		// as the corresponding EvGoCreate/EvGoUnblock, and thus the latter
   167  		// is already merged).
   168  		// seqinc is a stub for cases when event increments g sequence,
   169  		// but since we don't know current seq we also don't know next seq.
   170  		g = ev.G
   171  		init = gState{noseq, gRunnable}
   172  		next = gState{seqinc, gRunning}
   173  	case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
   174  		EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep,
   175  		EvGoSysBlock, EvGoBlockGC:
   176  		g = ev.G
   177  		init = gState{noseq, gRunning}
   178  		next = gState{noseq, gWaiting}
   179  	case EvGoSched, EvGoPreempt:
   180  		g = ev.G
   181  		init = gState{noseq, gRunning}
   182  		next = gState{noseq, gRunnable}
   183  	case EvGoUnblock, EvGoSysExit:
   184  		g = ev.Args[0]
   185  		init = gState{ev.Args[1], gWaiting}
   186  		next = gState{ev.Args[1] + 1, gRunnable}
   187  	case EvGoUnblockLocal, EvGoSysExitLocal:
   188  		g = ev.Args[0]
   189  		init = gState{noseq, gWaiting}
   190  		next = gState{seqinc, gRunnable}
   191  	case EvGCStart:
   192  		g = garbage
   193  		init = gState{ev.Args[0], gDead}
   194  		next = gState{ev.Args[0] + 1, gDead}
   195  	default:
   196  		// no ordering requirements
   197  		g = unordered
   198  	}
   199  	return
   200  }
   201  
   202  func transitionReady(g uint64, curr, init gState) bool {
   203  	return g == unordered || (init.seq == noseq || init.seq == curr.seq) && init.status == curr.status
   204  }
   205  
   206  func transition(gs map[uint64]gState, g uint64, init, next gState) {
   207  	if g == unordered {
   208  		return
   209  	}
   210  	curr := gs[g]
   211  	if !transitionReady(g, curr, init) {
   212  		panic("event sequences are broken")
   213  	}
   214  	switch next.seq {
   215  	case noseq:
   216  		next.seq = curr.seq
   217  	case seqinc:
   218  		next.seq = curr.seq + 1
   219  	}
   220  	gs[g] = next
   221  }
   222  
   223  // order1005 merges a set of per-P event batches into a single, consistent stream.
   224  func order1005(m map[int][]*Event) (events []*Event, err error) {
   225  	for _, batch := range m {
   226  		events = append(events, batch...)
   227  	}
   228  	for _, ev := range events {
   229  		if ev.Type == EvGoSysExit {
   230  			// EvGoSysExit emission is delayed until the thread has a P.
   231  			// Give it the real sequence number and time stamp.
   232  			ev.seq = int64(ev.Args[1])
   233  			if ev.Args[2] != 0 {
   234  				ev.Ts = int64(ev.Args[2])
   235  			}
   236  		}
   237  	}
   238  	sort.Sort(eventSeqList(events))
   239  	if !sort.IsSorted(eventList(events)) {
   240  		return nil, ErrTimeOrder
   241  	}
   242  	return
   243  }
   244  
   245  type orderEventList []orderEvent
   246  
   247  func (l orderEventList) Len() int {
   248  	return len(l)
   249  }
   250  
   251  func (l orderEventList) Less(i, j int) bool {
   252  	return l[i].ev.Ts < l[j].ev.Ts
   253  }
   254  
   255  func (l orderEventList) Swap(i, j int) {
   256  	l[i], l[j] = l[j], l[i]
   257  }
   258  
   259  type eventList []*Event
   260  
   261  func (l eventList) Len() int {
   262  	return len(l)
   263  }
   264  
   265  func (l eventList) Less(i, j int) bool {
   266  	return l[i].Ts < l[j].Ts
   267  }
   268  
   269  func (l eventList) Swap(i, j int) {
   270  	l[i], l[j] = l[j], l[i]
   271  }
   272  
   273  type eventSeqList []*Event
   274  
   275  func (l eventSeqList) Len() int {
   276  	return len(l)
   277  }
   278  
   279  func (l eventSeqList) Less(i, j int) bool {
   280  	return l[i].seq < l[j].seq
   281  }
   282  
   283  func (l eventSeqList) Swap(i, j int) {
   284  	l[i], l[j] = l[j], l[i]
   285  }