Home | History | Annotate | Download | only in trace
      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 }
    185