Source file
src/cmd/trace/goroutines.go
1
2
3
4
5
6
7 package main
8
9 import (
10 "fmt"
11 "html/template"
12 "internal/trace"
13 "log"
14 "net/http"
15 "reflect"
16 "sort"
17 "strconv"
18 "sync"
19 "time"
20 )
21
22 func init() {
23 http.HandleFunc("/goroutines", httpGoroutines)
24 http.HandleFunc("/goroutine", httpGoroutine)
25 }
26
27
28 type gtype struct {
29 ID uint64
30 Name string
31 N int
32 ExecTime int64
33 }
34
35 var (
36 gsInit sync.Once
37 gs map[uint64]*trace.GDesc
38 )
39
40
41 func analyzeGoroutines(events []*trace.Event) {
42 gsInit.Do(func() {
43 gs = trace.GoroutineStats(events)
44 })
45 }
46
47
48 func httpGoroutines(w http.ResponseWriter, r *http.Request) {
49 events, err := parseEvents()
50 if err != nil {
51 http.Error(w, err.Error(), http.StatusInternalServerError)
52 return
53 }
54 analyzeGoroutines(events)
55 gss := make(map[uint64]gtype)
56 for _, g := range gs {
57 gs1 := gss[g.PC]
58 gs1.ID = g.PC
59 gs1.Name = g.Name
60 gs1.N++
61 gs1.ExecTime += g.ExecTime
62 gss[g.PC] = gs1
63 }
64 var glist []gtype
65 for k, v := range gss {
66 v.ID = k
67 glist = append(glist, v)
68 }
69 sort.Slice(glist, func(i, j int) bool { return glist[i].ExecTime > glist[j].ExecTime })
70 w.Header().Set("Content-Type", "text/html;charset=utf-8")
71 if err := templGoroutines.Execute(w, glist); err != nil {
72 log.Printf("failed to execute template: %v", err)
73 return
74 }
75 }
76
77 var templGoroutines = template.Must(template.New("").Parse(`
78 <html>
79 <body>
80 Goroutines: <br>
81 {{range $}}
82 <a href="/goroutine?id={{.ID}}">{{.Name}}</a> N={{.N}} <br>
83 {{end}}
84 </body>
85 </html>
86 `))
87
88
89 func httpGoroutine(w http.ResponseWriter, r *http.Request) {
90
91
92 events, err := parseEvents()
93 if err != nil {
94 http.Error(w, err.Error(), http.StatusInternalServerError)
95 return
96 }
97
98 pc, err := strconv.ParseUint(r.FormValue("id"), 10, 64)
99 if err != nil {
100 http.Error(w, fmt.Sprintf("failed to parse id parameter '%v': %v", r.FormValue("id"), err), http.StatusInternalServerError)
101 return
102 }
103 analyzeGoroutines(events)
104 var (
105 glist []*trace.GDesc
106 name string
107 totalExecTime, execTime int64
108 maxTotalTime int64
109 )
110
111 for _, g := range gs {
112 totalExecTime += g.ExecTime
113
114 if g.PC != pc {
115 continue
116 }
117 glist = append(glist, g)
118 name = g.Name
119 execTime += g.ExecTime
120 if maxTotalTime < g.TotalTime {
121 maxTotalTime = g.TotalTime
122 }
123 }
124
125 execTimePercent := ""
126 if totalExecTime > 0 {
127 execTimePercent = fmt.Sprintf("%.2f%%", float64(execTime)/float64(totalExecTime)*100)
128 }
129
130 sortby := r.FormValue("sortby")
131 _, ok := reflect.TypeOf(trace.GDesc{}).FieldByNameFunc(func(s string) bool {
132 return s == sortby
133 })
134 if !ok {
135 sortby = "TotalTime"
136 }
137
138 sort.Slice(glist, func(i, j int) bool {
139 ival := reflect.ValueOf(glist[i]).Elem().FieldByName(sortby).Int()
140 jval := reflect.ValueOf(glist[j]).Elem().FieldByName(sortby).Int()
141 return ival > jval
142 })
143
144 err = templGoroutine.Execute(w, struct {
145 Name string
146 PC uint64
147 N int
148 ExecTimePercent string
149 MaxTotal int64
150 GList []*trace.GDesc
151 }{
152 Name: name,
153 PC: pc,
154 N: len(glist),
155 ExecTimePercent: execTimePercent,
156 MaxTotal: maxTotalTime,
157 GList: glist})
158 if err != nil {
159 http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError)
160 return
161 }
162 }
163
164 var templGoroutine = template.Must(template.New("").Funcs(template.FuncMap{
165 "prettyDuration": func(nsec int64) template.HTML {
166 d := time.Duration(nsec) * time.Nanosecond
167 return template.HTML(niceDuration(d))
168 },
169 "percent": func(dividend, divisor int64) template.HTML {
170 if divisor == 0 {
171 return ""
172 }
173 return template.HTML(fmt.Sprintf("(%.1f%%)", float64(dividend)/float64(divisor)*100))
174 },
175 "barLen": func(dividend, divisor int64) template.HTML {
176 if divisor == 0 {
177 return "0"
178 }
179 return template.HTML(fmt.Sprintf("%.2f%%", float64(dividend)/float64(divisor)*100))
180 },
181 "unknownTime": func(desc *trace.GDesc) int64 {
182 sum := desc.ExecTime + desc.IOTime + desc.BlockTime + desc.SyscallTime + desc.SchedWaitTime
183 if sum < desc.TotalTime {
184 return desc.TotalTime - sum
185 }
186 return 0
187 },
188 }).Parse(`
189 <!DOCTYPE html>
190 <title>Goroutine {{.Name}}</title>
191 <style>
192 th {
193 background-color: #050505;
194 color: #fff;
195 }
196 th.total-time,
197 th.exec-time,
198 th.io-time,
199 th.block-time,
200 th.syscall-time,
201 th.sched-time,
202 th.sweep-time,
203 th.pause-time {
204 cursor: pointer;
205 }
206 table {
207 border-collapse: collapse;
208 }
209 .details tr:hover {
210 background-color: #f2f2f2;
211 }
212 .details td {
213 text-align: right;
214 border: 1px solid black;
215 }
216 .details td.id {
217 text-align: left;
218 }
219 .stacked-bar-graph {
220 width: 300px;
221 height: 10px;
222 color: #414042;
223 white-space: nowrap;
224 font-size: 5px;
225 }
226 .stacked-bar-graph span {
227 display: inline-block;
228 width: 100%;
229 height: 100%;
230 box-sizing: border-box;
231 float: left;
232 padding: 0;
233 }
234 .unknown-time { background-color: #636363; }
235 .exec-time { background-color: #d7191c; }
236 .io-time { background-color: #fdae61; }
237 .block-time { background-color: #d01c8b; }
238 .syscall-time { background-color: #7b3294; }
239 .sched-time { background-color: #2c7bb6; }
240 </style>
241
242 <script>
243 function reloadTable(key, value) {
244 let params = new URLSearchParams(window.location.search);
245 params.set(key, value);
246 window.location.search = params.toString();
247 }
248 </script>
249
250 <table class="summary">
251 <tr><td>Goroutine Name:</td><td>{{.Name}}</td></tr>
252 <tr><td>Number of Goroutines:</td><td>{{.N}}</td></tr>
253 <tr><td>Execution Time:</td><td>{{.ExecTimePercent}} of total program execution time </td> </tr>
254 <tr><td>Network Wait Time:</td><td> <a href="/io?id={{.PC}}">graph</a><a href="/io?id={{.PC}}&raw=1" download="io.profile">(download)</a></td></tr>
255 <tr><td>Sync Block Time:</td><td> <a href="/block?id={{.PC}}">graph</a><a href="/block?id={{.PC}}&raw=1" download="block.profile">(download)</a></td></tr>
256 <tr><td>Blocking Syscall Time:</td><td> <a href="/syscall?id={{.PC}}">graph</a><a href="/syscall?id={{.PC}}&raw=1" download="syscall.profile">(download)</a></td></tr>
257 <tr><td>Scheduler Wait Time:</td><td> <a href="/sched?id={{.PC}}">graph</a><a href="/sched?id={{.PC}}&raw=1" download="sched.profile">(download)</a></td></tr>
258 </table>
259 <p>
260 <table class="details">
261 <tr>
262 <th> Goroutine</th>
263 <th onclick="reloadTable('sortby', 'TotalTime')" class="total-time"> Total</th>
264 <th></th>
265 <th onclick="reloadTable('sortby', 'ExecTime')" class="exec-time"> Execution</th>
266 <th onclick="reloadTable('sortby', 'IOTime')" class="io-time"> Network wait</th>
267 <th onclick="reloadTable('sortby', 'BlockTime')" class="block-time"> Sync block </th>
268 <th onclick="reloadTable('sortby', 'SyscallTime')" class="syscall-time"> Blocking syscall</th>
269 <th onclick="reloadTable('sortby', 'SchedWaitTime')" class="sched-time"> Scheduler wait</th>
270 <th onclick="reloadTable('sortby', 'SweepTime')" class="sweep-time"> GC sweeping</th>
271 <th onclick="reloadTable('sortby', 'GCTime')" class="pause-time"> GC pause</th>
272 </tr>
273 {{range .GList}}
274 <tr>
275 <td> <a href="/trace?goid={{.ID}}">{{.ID}}</a> </td>
276 <td> {{prettyDuration .TotalTime}} </td>
277 <td>
278 <div class="stacked-bar-graph">
279 {{if unknownTime .}}<span style="width:{{barLen (unknownTime .) $.MaxTotal}}" class="unknown-time"> </span>{{end}}
280 {{if .ExecTime}}<span style="width:{{barLen .ExecTime $.MaxTotal}}" class="exec-time"> </span>{{end}}
281 {{if .IOTime}}<span style="width:{{barLen .IOTime $.MaxTotal}}" class="io-time"> </span>{{end}}
282 {{if .BlockTime}}<span style="width:{{barLen .BlockTime $.MaxTotal}}" class="block-time"> </span>{{end}}
283 {{if .SyscallTime}}<span style="width:{{barLen .SyscallTime $.MaxTotal}}" class="syscall-time"> </span>{{end}}
284 {{if .SchedWaitTime}}<span style="width:{{barLen .SchedWaitTime $.MaxTotal}}" class="sched-time"> </span>{{end}}
285 </div>
286 </td>
287 <td> {{prettyDuration .ExecTime}}</td>
288 <td> {{prettyDuration .IOTime}}</td>
289 <td> {{prettyDuration .BlockTime}}</td>
290 <td> {{prettyDuration .SyscallTime}}</td>
291 <td> {{prettyDuration .SchedWaitTime}}</td>
292 <td> {{prettyDuration .SweepTime}} {{percent .SweepTime .TotalTime}}</td>
293 <td> {{prettyDuration .GCTime}} {{percent .GCTime .TotalTime}}</td>
294 </tr>
295 {{end}}
296 </table>
297 `))
298
View as plain text