...

Source file src/cmd/trace/goroutines.go

     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(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">&nbsp;</span>{{end}}
   270	          {{if .ExecTime}}<span style="width:{{barLen .ExecTime $.MaxTotal}}" class="exec-time">&nbsp;</span>{{end}}
   271	          {{if .IOTime}}<span style="width:{{barLen .IOTime $.MaxTotal}}" class="io-time">&nbsp;</span>{{end}}
   272	          {{if .BlockTime}}<span style="width:{{barLen .BlockTime $.MaxTotal}}" class="block-time">&nbsp;</span>{{end}}
   273	          {{if .SyscallTime}}<span style="width:{{barLen .SyscallTime $.MaxTotal}}" class="syscall-time">&nbsp;</span>{{end}}
   274	          {{if .SchedWaitTime}}<span style="width:{{barLen .SchedWaitTime $.MaxTotal}}" class="sched-time">&nbsp;</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