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:
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.
161func 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