1
2
3
4
5 package main
6
7 import (
8 "bufio"
9 "cmd/internal/browser"
10 cmdv2 "cmd/trace/v2"
11 "flag"
12 "fmt"
13 "internal/trace"
14 "internal/trace/traceviewer"
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=int: print debug info such as parsed events (1 for high-level, 2 for low-level)
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.Int("d", 0, "print debug information (1 for basic debug info, 2 for lower-level info)")
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 if isTraceV2(traceFile) {
87 if err := cmdv2.Main(traceFile, *httpFlag, *pprofFlag, *debugFlag); err != nil {
88 dief("%s\n", err)
89 }
90 return
91 }
92
93 var pprofFunc traceviewer.ProfileFunc
94 switch *pprofFlag {
95 case "net":
96 pprofFunc = pprofByGoroutine(computePprofIO)
97 case "sync":
98 pprofFunc = pprofByGoroutine(computePprofBlock)
99 case "syscall":
100 pprofFunc = pprofByGoroutine(computePprofSyscall)
101 case "sched":
102 pprofFunc = pprofByGoroutine(computePprofSched)
103 }
104 if pprofFunc != nil {
105 records, err := pprofFunc(&http.Request{})
106 if err != nil {
107 dief("failed to generate pprof: %v\n", err)
108 }
109 if err := traceviewer.BuildProfile(records).Write(os.Stdout); err != nil {
110 dief("failed to generate pprof: %v\n", err)
111 }
112 os.Exit(0)
113 }
114 if *pprofFlag != "" {
115 dief("unknown pprof type %s\n", *pprofFlag)
116 }
117
118 ln, err := net.Listen("tcp", *httpFlag)
119 if err != nil {
120 dief("failed to create server socket: %v\n", err)
121 }
122
123 log.Print("Parsing trace...")
124 res, err := parseTrace()
125 if err != nil {
126 dief("%v\n", err)
127 }
128
129 if *debugFlag != 0 {
130 trace.Print(res.Events)
131 os.Exit(0)
132 }
133 reportMemoryUsage("after parsing trace")
134 debug.FreeOSMemory()
135
136 log.Print("Splitting trace...")
137 ranges = splitTrace(res)
138 reportMemoryUsage("after splitting trace")
139 debug.FreeOSMemory()
140
141 addr := "http://" + ln.Addr().String()
142 log.Printf("Opening browser. Trace viewer is listening on %s", addr)
143 browser.Open(addr)
144
145
146 http.HandleFunc("/mmu", traceviewer.MMUHandlerFunc(ranges, mutatorUtil))
147
148
149 http.Handle("/", traceviewer.MainHandler([]traceviewer.View{
150 {Type: traceviewer.ViewProc, Ranges: ranges},
151 }))
152
153
154 err = http.Serve(ln, nil)
155 dief("failed to start http server: %v\n", err)
156 }
157
158
159 func isTraceV2(filename string) bool {
160 file, err := os.Open(filename)
161 if err != nil {
162 return false
163 }
164 defer file.Close()
165
166 ver, _, err := trace.ReadVersion(file)
167 if err != nil {
168 return false
169 }
170 return ver >= 1022
171 }
172
173 var ranges []traceviewer.Range
174
175 var loader struct {
176 once sync.Once
177 res trace.ParseResult
178 err error
179 }
180
181
182
183 func parseEvents() ([]*trace.Event, error) {
184 res, err := parseTrace()
185 if err != nil {
186 return nil, err
187 }
188 return res.Events, err
189 }
190
191 func parseTrace() (trace.ParseResult, error) {
192 loader.once.Do(func() {
193 tracef, err := os.Open(traceFile)
194 if err != nil {
195 loader.err = fmt.Errorf("failed to open trace file: %v", err)
196 return
197 }
198 defer tracef.Close()
199
200
201 res, err := trace.Parse(bufio.NewReader(tracef), programBinary)
202 if err != nil {
203 loader.err = fmt.Errorf("failed to parse trace: %v", err)
204 return
205 }
206 loader.res = res
207 })
208 return loader.res, loader.err
209 }
210
211 func dief(msg string, args ...any) {
212 fmt.Fprintf(os.Stderr, msg, args...)
213 os.Exit(1)
214 }
215
216 var debugMemoryUsage bool
217
218 func init() {
219 v := os.Getenv("DEBUG_MEMORY_USAGE")
220 debugMemoryUsage = v != ""
221 }
222
223 func reportMemoryUsage(msg string) {
224 if !debugMemoryUsage {
225 return
226 }
227 var s runtime.MemStats
228 runtime.ReadMemStats(&s)
229 w := os.Stderr
230 fmt.Fprintf(w, "%s\n", msg)
231 fmt.Fprintf(w, " Alloc:\t%d Bytes\n", s.Alloc)
232 fmt.Fprintf(w, " Sys:\t%d Bytes\n", s.Sys)
233 fmt.Fprintf(w, " HeapReleased:\t%d Bytes\n", s.HeapReleased)
234 fmt.Fprintf(w, " HeapSys:\t%d Bytes\n", s.HeapSys)
235 fmt.Fprintf(w, " HeapInUse:\t%d Bytes\n", s.HeapInuse)
236 fmt.Fprintf(w, " HeapAlloc:\t%d Bytes\n", s.HeapAlloc)
237 var dummy string
238 fmt.Printf("Enter to continue...")
239 fmt.Scanf("%s", &dummy)
240 }
241
242 func mutatorUtil(flags trace.UtilFlags) ([][]trace.MutatorUtil, error) {
243 events, err := parseEvents()
244 if err != nil {
245 return nil, err
246 }
247 return trace.MutatorUtilization(events, flags), nil
248 }
249
View as plain text