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:
     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 EvGoUnblock:
     87 			g := gs[ev.Args[0]]
     88 			if g.blockNetTime != 0 {
     89 				g.IOTime += ev.Ts - g.blockNetTime
     90 				g.blockNetTime = 0
     91 			}
     92 			if g.blockSyncTime != 0 {
     93 				g.BlockTime += ev.Ts - g.blockSyncTime
     94 				g.blockSyncTime = 0
     95 			}
     96 			g.blockSchedTime = ev.Ts
     97 		case EvGoSysBlock:
     98 			g := gs[ev.G]
     99 			g.ExecTime += ev.Ts - g.lastStartTime
    100 			g.blockSyscallTime = ev.Ts
    101 		case EvGoSysExit:
    102 			g := gs[ev.G]
    103 			if g.blockSyscallTime != 0 {
    104 				g.SyscallTime += ev.Ts - g.blockSyscallTime
    105 				g.blockSyscallTime = 0
    106 			}
    107 			g.blockSchedTime = ev.Ts
    108 		case EvGCSweepStart:
    109 			g := gs[ev.G]
    110 			if g != nil {
    111 				// Sweep can happen during GC on system goroutine.
    112 				g.blockSweepTime = ev.Ts
    113 			}
    114 		case EvGCSweepDone:
    115 			g := gs[ev.G]
    116 			if g != nil && g.blockSweepTime != 0 {
    117 				g.SweepTime += ev.Ts - g.blockSweepTime
    118 				g.blockSweepTime = 0
    119 			}
    120 		case EvGCStart:
    121 			gcStartTime = ev.Ts
    122 		case EvGCDone:
    123 			for _, g := range gs {
    124 				if g.EndTime == 0 {
    125 					g.GCTime += ev.Ts - gcStartTime
    126 				}
    127 			}
    128 		}
    129 	}
    130 
    131 	for _, g := range gs {
    132 		if g.TotalTime == 0 {
    133 			g.TotalTime = lastTs - g.CreationTime
    134 		}
    135 		if g.EndTime == 0 {
    136 			g.EndTime = lastTs
    137 		}
    138 		if g.blockNetTime != 0 {
    139 			g.IOTime += lastTs - g.blockNetTime
    140 			g.blockNetTime = 0
    141 		}
    142 		if g.blockSyncTime != 0 {
    143 			g.BlockTime += lastTs - g.blockSyncTime
    144 			g.blockSyncTime = 0
    145 		}
    146 		if g.blockSyscallTime != 0 {
    147 			g.SyscallTime += lastTs - g.blockSyscallTime
    148 			g.blockSyscallTime = 0
    149 		}
    150 		if g.blockSchedTime != 0 {
    151 			g.SchedWaitTime += lastTs - g.blockSchedTime
    152 			g.blockSchedTime = 0
    153 		}
    154 		g.gdesc = nil
    155 	}
    156 
    157 	return gs
    158 }
    159 
    160 // RelatedGoroutines finds a set of goroutines related to goroutine goid.
    161 func RelatedGoroutines(events []*Event, goid uint64) map[uint64]bool {
    162 	// BFS of depth 2 over "unblock" edges
    163 	// (what goroutines unblock goroutine goid?).
    164 	gmap := make(map[uint64]bool)
    165 	gmap[goid] = true
    166 	for i := 0; i < 2; i++ {
    167 		gmap1 := make(map[uint64]bool)
    168 		for g := range gmap {
    169 			gmap1[g] = true
    170 		}
    171 		for _, ev := range events {
    172 			if ev.Type == EvGoUnblock && gmap[ev.Args[0]] {
    173 				gmap1[ev.G] = true
    174 			}
    175 		}
    176 		gmap = gmap1
    177 	}
    178 	gmap[0] = true // for GC events
    179 	return gmap
    180 }
    181