1
2
3
4
5 package main
6
7 import (
8 "bufio"
9 "cmd/internal/browser"
10 "flag"
11 "fmt"
12 "html/template"
13 "internal/trace"
14 "io"
15 "log"
16 "net"
17 "net/http"
18 "os"
19 "runtime"
20 "runtime/debug"
21 "sync"
22
23 _ "net/http/pprof"
24 )
25
26 const usageMessage = "" +
27 `Usage of 'go tool trace':
28 Given a trace file produced by 'go test':
29 go test -trace=trace.out pkg
30
31 Open a web browser displaying trace:
32 go tool trace [flags] [pkg.test] trace.out
33
34 Generate a pprof-like profile from the trace:
35 go tool trace -pprof=TYPE [pkg.test] trace.out
36
37 [pkg.test] argument is required for traces produced by Go 1.6 and below.
38 Go 1.7 does not require the binary argument.
39
40 Supported profile types are:
41 - net: network blocking profile
42 - sync: synchronization blocking profile
43 - syscall: syscall blocking profile
44 - sched: scheduler latency profile
45
46 Flags:
47 -http=addr: HTTP service address (e.g., ':6060')
48 -pprof=type: print a pprof-like profile instead
49 -d: print debug info such as parsed events
50
51 Note that while the various profiles available when launching
52 'go tool trace' work on every browser, the trace viewer itself
53 (the 'view trace' page) comes from the Chrome/Chromium project
54 and is only actively tested on that browser.
55 `
56
57 var (
58 httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')")
59 pprofFlag = flag.String("pprof", "", "print a pprof-like profile instead")
60 debugFlag = flag.Bool("d", false, "print debug information such as parsed events list")
61
62
63 programBinary string
64 traceFile string
65 )
66
67 func main() {
68 flag.Usage = func() {
69 fmt.Fprint(os.Stderr, usageMessage)
70 os.Exit(2)
71 }
72 flag.Parse()
73
74
75
76 switch flag.NArg() {
77 case 1:
78 traceFile = flag.Arg(0)
79 case 2:
80 programBinary = flag.Arg(0)
81 traceFile = flag.Arg(1)
82 default:
83 flag.Usage()
84 }
85
86 var pprofFunc func(io.Writer, *http.Request) error
87 switch *pprofFlag {
88 case "net":
89 pprofFunc = pprofByGoroutine(computePprofIO)
90 case "sync":
91 pprofFunc = pprofByGoroutine(computePprofBlock)
92 case "syscall":
93 pprofFunc = pprofByGoroutine(computePprofSyscall)
94 case "sched":
95 pprofFunc = pprofByGoroutine(computePprofSched)
96 }
97 if pprofFunc != nil {
98 if err := pprofFunc(os.Stdout, &http.Request{}); err != nil {
99 dief("failed to generate pprof: %v\n", err)
100 }
101 os.Exit(0)
102 }
103 if *pprofFlag != "" {
104 dief("unknown pprof type %s\n", *pprofFlag)
105 }
106
107 ln, err := net.Listen("tcp", *httpFlag)
108 if err != nil {
109 dief("failed to create server socket: %v\n", err)
110 }
111
112 log.Print("Parsing trace...")
113 res, err := parseTrace()
114 if err != nil {
115 dief("%v\n", err)
116 }
117
118 if *debugFlag {
119 trace.Print(res.Events)
120 os.Exit(0)
121 }
122 reportMemoryUsage("after parsing trace")
123 debug.FreeOSMemory()
124
125 log.Print("Splitting trace...")
126 ranges = splitTrace(res)
127 reportMemoryUsage("after spliting trace")
128 debug.FreeOSMemory()
129
130 addr := "http://" + ln.Addr().String()
131 log.Printf("Opening browser. Trace viewer is listening on %s", addr)
132 browser.Open(addr)
133
134
135 http.HandleFunc("/", httpMain)
136 err = http.Serve(ln, nil)
137 dief("failed to start http server: %v\n", err)
138 }
139
140 var ranges []Range
141
142 var loader struct {
143 once sync.Once
144 res trace.ParseResult
145 err error
146 }
147
148
149
150 func parseEvents() ([]*trace.Event, error) {
151 res, err := parseTrace()
152 if err != nil {
153 return nil, err
154 }
155 return res.Events, err
156 }
157
158 func parseTrace() (trace.ParseResult, error) {
159 loader.once.Do(func() {
160 tracef, err := os.Open(traceFile)
161 if err != nil {
162 loader.err = fmt.Errorf("failed to open trace file: %v", err)
163 return
164 }
165 defer tracef.Close()
166
167
168 res, err := trace.Parse(bufio.NewReader(tracef), programBinary)
169 if err != nil {
170 loader.err = fmt.Errorf("failed to parse trace: %v", err)
171 return
172 }
173 loader.res = res
174 })
175 return loader.res, loader.err
176 }
177
178
179 func httpMain(w http.ResponseWriter, r *http.Request) {
180 if err := templMain.Execute(w, ranges); err != nil {
181 http.Error(w, err.Error(), http.StatusInternalServerError)
182 return
183 }
184 }
185
186 var templMain = template.Must(template.New("").Parse(`
187 <html>
188 <body>
189 {{if $}}
190 {{range $e := $}}
191 <a href="{{$e.URL}}">View trace ({{$e.Name}})</a><br>
192 {{end}}
193 <br>
194 {{else}}
195 <a href="/trace">View trace</a><br>
196 {{end}}
197 <a href="/goroutines">Goroutine analysis</a><br>
198 <a href="/io">Network blocking profile</a> (<a href="/io?raw=1" download="io.profile">⬇</a>)<br>
199 <a href="/block">Synchronization blocking profile</a> (<a href="/block?raw=1" download="block.profile">⬇</a>)<br>
200 <a href="/syscall">Syscall blocking profile</a> (<a href="/syscall?raw=1" download="syscall.profile">⬇</a>)<br>
201 <a href="/sched">Scheduler latency profile</a> (<a href="/sche?raw=1" download="sched.profile">⬇</a>)<br>
202 <a href="/usertasks">User-defined tasks</a><br>
203 <a href="/userregions">User-defined regions</a><br>
204 <a href="/mmu">Minimum mutator utilization</a><br>
205 </body>
206 </html>
207 `))
208
209 func dief(msg string, args ...any) {
210 fmt.Fprintf(os.Stderr, msg, args...)
211 os.Exit(1)
212 }
213
214 var debugMemoryUsage bool
215
216 func init() {
217 v := os.Getenv("DEBUG_MEMORY_USAGE")
218 debugMemoryUsage = v != ""
219 }
220
221 func reportMemoryUsage(msg string) {
222 if !debugMemoryUsage {
223 return
224 }
225 var s runtime.MemStats
226 runtime.ReadMemStats(&s)
227 w := os.Stderr
228 fmt.Fprintf(w, "%s\n", msg)
229 fmt.Fprintf(w, " Alloc:\t%d Bytes\n", s.Alloc)
230 fmt.Fprintf(w, " Sys:\t%d Bytes\n", s.Sys)
231 fmt.Fprintf(w, " HeapReleased:\t%d Bytes\n", s.HeapReleased)
232 fmt.Fprintf(w, " HeapSys:\t%d Bytes\n", s.HeapSys)
233 fmt.Fprintf(w, " HeapInUse:\t%d Bytes\n", s.HeapInuse)
234 fmt.Fprintf(w, " HeapAlloc:\t%d Bytes\n", s.HeapAlloc)
235 var dummy string
236 fmt.Printf("Enter to continue...")
237 fmt.Scanf("%s", &dummy)
238 }
239
View as plain text