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
5package trace
6
7// GDesc contains statistics about execution of a single goroutine.
8type 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.
29type 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.
40func 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.
165func 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