Source file
src/cmd/trace/pprof.go
1
2
3
4
5
6
7 package main
8
9 import (
10 "fmt"
11 "internal/trace"
12 "internal/trace/traceviewer"
13 "net/http"
14 "sort"
15 "strconv"
16 "time"
17 )
18
19 func init() {
20 http.HandleFunc("/io", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofIO)))
21 http.HandleFunc("/block", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofBlock)))
22 http.HandleFunc("/syscall", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSyscall)))
23 http.HandleFunc("/sched", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSched)))
24
25 http.HandleFunc("/regionio", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofIO)))
26 http.HandleFunc("/regionblock", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofBlock)))
27 http.HandleFunc("/regionsyscall", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSyscall)))
28 http.HandleFunc("/regionsched", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSched)))
29 }
30
31
32 type interval struct {
33 begin, end int64
34 }
35
36 func pprofByGoroutine(compute computePprofFunc) traceviewer.ProfileFunc {
37 return func(r *http.Request) ([]traceviewer.ProfileRecord, error) {
38 id := r.FormValue("id")
39 events, err := parseEvents()
40 if err != nil {
41 return nil, err
42 }
43 gToIntervals, err := pprofMatchingGoroutines(id, events)
44 if err != nil {
45 return nil, err
46 }
47 return compute(gToIntervals, events)
48 }
49 }
50
51 func pprofByRegion(compute computePprofFunc) traceviewer.ProfileFunc {
52 return func(r *http.Request) ([]traceviewer.ProfileRecord, error) {
53 filter, err := newRegionFilter(r)
54 if err != nil {
55 return nil, err
56 }
57 gToIntervals, err := pprofMatchingRegions(filter)
58 if err != nil {
59 return nil, err
60 }
61 events, _ := parseEvents()
62
63 return compute(gToIntervals, events)
64 }
65 }
66
67
68
69
70 func pprofMatchingGoroutines(id string, events []*trace.Event) (map[uint64][]interval, error) {
71 if id == "" {
72 return nil, nil
73 }
74 pc, err := strconv.ParseUint(id, 10, 64)
75 if err != nil {
76 return nil, fmt.Errorf("invalid goroutine type: %v", id)
77 }
78 analyzeGoroutines(events)
79 var res map[uint64][]interval
80 for _, g := range gs {
81 if g.PC != pc {
82 continue
83 }
84 if res == nil {
85 res = make(map[uint64][]interval)
86 }
87 endTime := g.EndTime
88 if g.EndTime == 0 {
89 endTime = lastTimestamp()
90 }
91 res[g.ID] = []interval{{begin: g.StartTime, end: endTime}}
92 }
93 if len(res) == 0 && id != "" {
94 return nil, fmt.Errorf("failed to find matching goroutines for id: %s", id)
95 }
96 return res, nil
97 }
98
99
100
101 func pprofMatchingRegions(filter *regionFilter) (map[uint64][]interval, error) {
102 res, err := analyzeAnnotations()
103 if err != nil {
104 return nil, err
105 }
106 if filter == nil {
107 return nil, nil
108 }
109
110 gToIntervals := make(map[uint64][]interval)
111 for id, regions := range res.regions {
112 for _, s := range regions {
113 if filter.match(id, s) {
114 gToIntervals[s.G] = append(gToIntervals[s.G], interval{begin: s.firstTimestamp(), end: s.lastTimestamp()})
115 }
116 }
117 }
118
119 for g, intervals := range gToIntervals {
120
121
122
123
124 sort.Slice(intervals, func(i, j int) bool {
125 x := intervals[i].begin
126 y := intervals[j].begin
127 if x == y {
128 return intervals[i].end < intervals[j].end
129 }
130 return x < y
131 })
132 var lastTimestamp int64
133 var n int
134
135 for _, i := range intervals {
136 if lastTimestamp <= i.begin {
137 intervals[n] = i
138 lastTimestamp = i.end
139 n++
140 }
141 }
142 gToIntervals[g] = intervals[:n]
143 }
144 return gToIntervals, nil
145 }
146
147 type computePprofFunc func(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error)
148
149
150 func computePprofIO(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) {
151 prof := make(map[uint64]traceviewer.ProfileRecord)
152 for _, ev := range events {
153 if ev.Type != trace.EvGoBlockNet || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
154 continue
155 }
156 overlapping := pprofOverlappingDuration(gToIntervals, ev)
157 if overlapping > 0 {
158 rec := prof[ev.StkID]
159 rec.Stack = ev.Stk
160 rec.Count++
161 rec.Time += overlapping
162 prof[ev.StkID] = rec
163 }
164 }
165 return recordsOf(prof), nil
166 }
167
168
169 func computePprofBlock(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) {
170 prof := make(map[uint64]traceviewer.ProfileRecord)
171 for _, ev := range events {
172 switch ev.Type {
173 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect,
174 trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockGC:
175
176
177
178 default:
179 continue
180 }
181 if ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
182 continue
183 }
184 overlapping := pprofOverlappingDuration(gToIntervals, ev)
185 if overlapping > 0 {
186 rec := prof[ev.StkID]
187 rec.Stack = ev.Stk
188 rec.Count++
189 rec.Time += overlapping
190 prof[ev.StkID] = rec
191 }
192 }
193 return recordsOf(prof), nil
194 }
195
196
197 func computePprofSyscall(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) {
198 prof := make(map[uint64]traceviewer.ProfileRecord)
199 for _, ev := range events {
200 if ev.Type != trace.EvGoSysCall || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
201 continue
202 }
203 overlapping := pprofOverlappingDuration(gToIntervals, ev)
204 if overlapping > 0 {
205 rec := prof[ev.StkID]
206 rec.Stack = ev.Stk
207 rec.Count++
208 rec.Time += overlapping
209 prof[ev.StkID] = rec
210 }
211 }
212 return recordsOf(prof), nil
213 }
214
215
216
217 func computePprofSched(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) {
218 prof := make(map[uint64]traceviewer.ProfileRecord)
219 for _, ev := range events {
220 if (ev.Type != trace.EvGoUnblock && ev.Type != trace.EvGoCreate) ||
221 ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
222 continue
223 }
224 overlapping := pprofOverlappingDuration(gToIntervals, ev)
225 if overlapping > 0 {
226 rec := prof[ev.StkID]
227 rec.Stack = ev.Stk
228 rec.Count++
229 rec.Time += overlapping
230 prof[ev.StkID] = rec
231 }
232 }
233 return recordsOf(prof), nil
234 }
235
236
237
238
239 func pprofOverlappingDuration(gToIntervals map[uint64][]interval, ev *trace.Event) time.Duration {
240 if gToIntervals == nil {
241 return time.Duration(ev.Link.Ts-ev.Ts) * time.Nanosecond
242 }
243 intervals := gToIntervals[ev.G]
244 if len(intervals) == 0 {
245 return 0
246 }
247
248 var overlapping time.Duration
249 for _, i := range intervals {
250 if o := overlappingDuration(i.begin, i.end, ev.Ts, ev.Link.Ts); o > 0 {
251 overlapping += o
252 }
253 }
254 return overlapping
255 }
256
257 func recordsOf(records map[uint64]traceviewer.ProfileRecord) []traceviewer.ProfileRecord {
258 result := make([]traceviewer.ProfileRecord, 0, len(records))
259 for _, record := range records {
260 result = append(result, record)
261 }
262 return result
263 }
264
View as plain text