github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/cmd/trace/goroutines.go (about) 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 5 // Goroutine-related profiles. 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 // gtype describes a group of goroutines grouped by start PC. 28 type gtype struct { 29 ID uint64 // Unique identifier (PC). 30 Name string // Start function. 31 N int // Total number of goroutines in this group. 32 ExecTime int64 // Total execution time of all goroutines in this group. 33 } 34 35 var ( 36 gsInit sync.Once 37 gs map[uint64]*trace.GDesc 38 ) 39 40 // analyzeGoroutines generates statistics about execution of all goroutines and stores them in gs. 41 func analyzeGoroutines(events []*trace.Event) { 42 gsInit.Do(func() { 43 gs = trace.GoroutineStats(events) 44 }) 45 } 46 47 // httpGoroutines serves list of goroutine groups. 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 // httpGoroutine serves list of goroutines in a particular group. 89 func httpGoroutine(w http.ResponseWriter, r *http.Request) { 90 // TODO(hyangah): support format=csv (raw data) 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(dividened, divisor int64) template.HTML { 170 if divisor == 0 { 171 return "" 172 } 173 return template.HTML(fmt.Sprintf("(%.1f%%)", float64(dividened)/float64(divisor)*100)) 174 }, 175 "barLen": func(dividened, divisor int64) template.HTML { 176 if divisor == 0 { 177 return "0" 178 } 179 return template.HTML(fmt.Sprintf("%.2f%%", float64(dividened)/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 `))