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 table {
197 border-collapse: collapse;
198 }
199 .details tr:hover {
200 background-color: #f2f2f2;
201 }
202 .details td {
203 text-align: right;
204 border: 1px solid black;
205 }
206 .details td.id {
207 text-align: left;
208 }
209 .stacked-bar-graph {
210 width: 300px;
211 height: 10px;
212 color: #414042;
213 white-space: nowrap;
214 font-size: 5px;
215 }
216 .stacked-bar-graph span {
217 display: inline-block;
218 width: 100%;
219 height: 100%;
220 box-sizing: border-box;
221 float: left;
222 padding: 0;
223 }
224 .unknown-time { background-color: #636363; }
225 .exec-time { background-color: #d7191c; }
226 .io-time { background-color: #fdae61; }
227 .block-time { background-color: #d01c8b; }
228 .syscall-time { background-color: #7b3294; }
229 .sched-time { background-color: #2c7bb6; }
230 </style>
231
232 <script>
233 function reloadTable(key, value) {
234 let params = new URLSearchParams(window.location.search);
235 params.set(key, value);
236 window.location.search = params.toString();
237 }
238 </script>
239
240 <table class="summary">
241 <tr><td>Goroutine Name:</td><td>{{.Name}}</td></tr>
242 <tr><td>Number of Goroutines:</td><td>{{.N}}</td></tr>
243 <tr><td>Execution Time:</td><td>{{.ExecTimePercent}} of total program execution time </td> </tr>
244 <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>
245 <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>
246 <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>
247 <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>
248 </table>
249 <p>
250 <table class="details">
251 <tr>
252 <th> Goroutine</th>
253 <th onclick="reloadTable('sortby', 'TotalTime')"> Total</th>
254 <th></th>
255 <th onclick="reloadTable('sortby', 'ExecTime')" class="exec-time"> Execution</th>
256 <th onclick="reloadTable('sortby', 'IOTime')" class="io-time"> Network wait</th>
257 <th onclick="reloadTable('sortby', 'BlockTime')" class="block-time"> Sync block </th>
258 <th onclick="reloadTable('sortby', 'SyscallTime')" class="syscall-time"> Blocking syscall</th>
259 <th onclick="reloadTable('sortby', 'SchedWaitTime')" class="sched-time"> Scheduler wait</th>
260 <th onclick="reloadTable('sortby', 'SweepTime')"> GC sweeping</th>
261 <th onclick="reloadTable('sortby', 'GCTime')"> GC pause</th>
262 </tr>
263 {{range .GList}}
264 <tr>
265 <td> <a href="/trace?goid={{.ID}}">{{.ID}}</a> </td>
266 <td> {{prettyDuration .TotalTime}} </td>
267 <td>
268 <div class="stacked-bar-graph">
269 {{if unknownTime .}}<span style="width:{{barLen (unknownTime .) $.MaxTotal}}" class="unknown-time"> </span>{{end}}
270 {{if .ExecTime}}<span style="width:{{barLen .ExecTime $.MaxTotal}}" class="exec-time"> </span>{{end}}
271 {{if .IOTime}}<span style="width:{{barLen .IOTime $.MaxTotal}}" class="io-time"> </span>{{end}}
272 {{if .BlockTime}}<span style="width:{{barLen .BlockTime $.MaxTotal}}" class="block-time"> </span>{{end}}
273 {{if .SyscallTime}}<span style="width:{{barLen .SyscallTime $.MaxTotal}}" class="syscall-time"> </span>{{end}}
274 {{if .SchedWaitTime}}<span style="width:{{barLen .SchedWaitTime $.MaxTotal}}" class="sched-time"> </span>{{end}}
275 </div>
276 </td>
277 <td> {{prettyDuration .ExecTime}}</td>
278 <td> {{prettyDuration .IOTime}}</td>
279 <td> {{prettyDuration .BlockTime}}</td>
280 <td> {{prettyDuration .SyscallTime}}</td>
281 <td> {{prettyDuration .SchedWaitTime}}</td>
282 <td> {{prettyDuration .SweepTime}} {{percent .SweepTime .TotalTime}}</td>
283 <td> {{prettyDuration .GCTime}} {{percent .GCTime .TotalTime}}</td>
284 </tr>
285 {{end}}
286 </table>
287 `))
288
View as plain text