github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/src/internal/trace/goroutines.go (about)

     1  // Copyright 2014 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  // GDesc contains statistics about execution of a single goroutine.
     8  type GDesc struct {
     9  	ID           uint64
    10  	Name         string
    11  	PC           uint64
    12  	CreationTime int64
    13  	StartTime    int64
    14  	EndTime      int64
    15  
    16  	ExecTime      int64
    17  	SchedWaitTime int64
    18  	IOTime        int64
    19  	BlockTime     int64
    20  	SyscallTime   int64
    21  	GCTime        int64
    22  	SweepTime     int64
    23  	TotalTime     int64
    24  
    25  	*gdesc // private part
    26  }
    27  
    28  // gdesc is a private part of GDesc that is required only during analysis.
    29  type gdesc struct {
    30  	lastStartTime    int64
    31  	blockNetTime     int64
    32  	blockSyncTime    int64
    33  	blockSyscallTime int64
    34  	blockSweepTime   int64
    35  	blockGCTime      int64
    36  	blockSchedTime   int64
    37  }
    38  
    39  // GoroutineStats generates statistics for all goroutines in the trace.
    40  func GoroutineStats(events []*Event) map[uint64]*GDesc {
    41  	gs := make(map[uint64]*GDesc)
    42  	var lastTs int64
    43  	var gcStartTime int64
    44  	for _, ev := range events {
    45  		lastTs = ev.Ts
    46  		switch ev.Type {
    47  		case EvGoCreate:
    48  			g := &GDesc{ID: ev.Args[0], CreationTime: ev.Ts, gdesc: new(gdesc)}
    49  			g.blockSchedTime = ev.Ts
    50  			gs[g.ID] = g
    51  		case EvGoStart, EvGoStartLabel:
    52  			g := gs[ev.G]
    53  			if g.PC == 0 {
    54  				g.PC = ev.Stk[0].PC
    55  				g.Name = ev.Stk[0].Fn
    56  			}
    57  			g.lastStartTime = ev.Ts
    58  			if g.StartTime == 0 {
    59  				g.StartTime = ev.Ts
    60  			}
    61  			if g.blockSchedTime != 0 {
    62  				g.SchedWaitTime += ev.Ts - g.blockSchedTime
    63  				g.blockSchedTime = 0
    64  			}
    65  		case EvGoEnd, EvGoStop:
    66  			g := gs[ev.G]
    67  			g.ExecTime += ev.Ts - g.lastStartTime
    68  			g.TotalTime = ev.Ts - g.CreationTime
    69  			g.EndTime = ev.Ts
    70  		case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
    71  			EvGoBlockSync, EvGoBlockCond:
    72  			g := gs[ev.G]
    73  			g.ExecTime += ev.Ts - g.lastStartTime
    74  			g.blockSyncTime = ev.Ts
    75  		case EvGoSched, EvGoPreempt:
    76  			g := gs[ev.G]
    77  			g.ExecTime += ev.Ts - g.lastStartTime
    78  			g.blockSchedTime = ev.Ts
    79  		case EvGoSleep, EvGoBlock:
    80  			g := gs[ev.G]
    81  			g.ExecTime += ev.Ts - g.lastStartTime
    82  		case EvGoBlockNet:
    83  			g := gs[ev.G]
    84  			g.ExecTime += ev.Ts - g.lastStartTime
    85  			g.blockNetTime = ev.Ts
    86  		case EvGoBlockGC:
    87  			g := gs[ev.G]
    88  			g.ExecTime += ev.Ts - g.lastStartTime
    89  			g.blockGCTime = ev.Ts
    90  		case EvGoUnblock:
    91  			g := gs[ev.Args[0]]
    92  			if g.blockNetTime != 0 {
    93  				g.IOTime += ev.Ts - g.blockNetTime
    94  				g.blockNetTime = 0
    95  			}
    96  			if g.blockSyncTime != 0 {
    97  				g.BlockTime += ev.Ts - g.blockSyncTime
    98  				g.blockSyncTime = 0
    99  			}
   100  			g.blockSchedTime = ev.Ts
   101  		case EvGoSysBlock:
   102  			g := gs[ev.G]
   103  			g.ExecTime += ev.Ts - g.lastStartTime
   104  			g.blockSyscallTime = ev.Ts
   105  		case EvGoSysExit:
   106  			g := gs[ev.G]
   107  			if g.blockSyscallTime != 0 {
   108  				g.SyscallTime += ev.Ts - g.blockSyscallTime
   109  				g.blockSyscallTime = 0
   110  			}
   111  			g.blockSchedTime = ev.Ts
   112  		case EvGCSweepStart:
   113  			g := gs[ev.G]
   114  			if g != nil {
   115  				// Sweep can happen during GC on system goroutine.
   116  				g.blockSweepTime = ev.Ts
   117  			}
   118  		case EvGCSweepDone:
   119  			g := gs[ev.G]
   120  			if g != nil && g.blockSweepTime != 0 {
   121  				g.SweepTime += ev.Ts - g.blockSweepTime
   122  				g.blockSweepTime = 0
   123  			}
   124  		case EvGCStart:
   125  			gcStartTime = ev.Ts
   126  		case EvGCDone:
   127  			for _, g := range gs {
   128  				if g.EndTime == 0 {
   129  					g.GCTime += ev.Ts - gcStartTime
   130  				}
   131  			}
   132  		}
   133  	}
   134  
   135  	for _, g := range gs {
   136  		if g.TotalTime == 0 {
   137  			g.TotalTime = lastTs - g.CreationTime
   138  		}
   139  		if g.EndTime == 0 {
   140  			g.EndTime = lastTs
   141  		}
   142  		if g.blockNetTime != 0 {
   143  			g.IOTime += lastTs - g.blockNetTime
   144  			g.blockNetTime = 0
   145  		}
   146  		if g.blockSyncTime != 0 {
   147  			g.BlockTime += lastTs - g.blockSyncTime
   148  			g.blockSyncTime = 0
   149  		}
   150  		if g.blockSyscallTime != 0 {
   151  			g.SyscallTime += lastTs - g.blockSyscallTime
   152  			g.blockSyscallTime = 0
   153  		}
   154  		if g.blockSchedTime != 0 {
   155  			g.SchedWaitTime += lastTs - g.blockSchedTime
   156  			g.blockSchedTime = 0
   157  		}
   158  		g.gdesc = nil
   159  	}
   160  
   161  	return gs
   162  }
   163  
   164  // RelatedGoroutines finds a set of goroutines related to goroutine goid.
   165  func RelatedGoroutines(events []*Event, goid uint64) map[uint64]bool {
   166  	// BFS of depth 2 over "unblock" edges
   167  	// (what goroutines unblock goroutine goid?).
   168  	gmap := make(map[uint64]bool)
   169  	gmap[goid] = true
   170  	for i := 0; i < 2; i++ {
   171  		gmap1 := make(map[uint64]bool)
   172  		for g := range gmap {
   173  			gmap1[g] = true
   174  		}
   175  		for _, ev := range events {
   176  			if ev.Type == EvGoUnblock && gmap[ev.Args[0]] {
   177  				gmap1[ev.G] = true
   178  			}
   179  		}
   180  		gmap = gmap1
   181  	}
   182  	gmap[0] = true // for GC events
   183  	return gmap
   184  }