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 /* 6 Trace is a tool for viewing trace files. 7 8 Trace files can be generated with: 9 - runtime/trace.Start 10 - net/http/pprof package 11 - go test -trace 12 13 Example usage: 14 Generate a trace file with 'go test': 15 go test -trace trace.out pkg 16 View the trace in a web browser: 17 go tool trace pkg.test trace.out 18 */ 19 package main 20 21 import ( 22 "bufio" 23 "flag" 24 "fmt" 25 "internal/trace" 26 "net" 27 "net/http" 28 "os" 29 "os/exec" 30 "runtime" 31 "sync" 32 ) 33 34 const usageMessage = "" + 35 `Usage of 'go tool trace': 36 Given a trace file produced by 'go test': 37 go test -trace=trace.out pkg 38 39 Open a web browser displaying trace: 40 go tool trace [flags] pkg.test trace.out 41 42 Flags: 43 -http=addr: HTTP service address (e.g., ':6060') 44 ` 45 46 var ( 47 httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')") 48 49 // The binary file name, left here for serveSVGProfile. 50 programBinary string 51 traceFile string 52 ) 53 54 func main() { 55 flag.Usage = func() { 56 fmt.Fprintln(os.Stderr, usageMessage) 57 os.Exit(2) 58 } 59 flag.Parse() 60 61 // Usage information when no arguments. 62 if flag.NArg() != 2 { 63 flag.Usage() 64 } 65 programBinary = flag.Arg(0) 66 traceFile = flag.Arg(1) 67 68 ln, err := net.Listen("tcp", *httpFlag) 69 if err != nil { 70 dief("failed to create server socket: %v\n", err) 71 } 72 // Open browser. 73 if !startBrowser("http://" + ln.Addr().String()) { 74 dief("failed to start browser\n") 75 } 76 77 // Parse and symbolize trace asynchronously while browser opens. 78 go parseEvents() 79 80 // Start http server. 81 http.HandleFunc("/", httpMain) 82 err = http.Serve(ln, nil) 83 dief("failed to start http server: %v\n", err) 84 } 85 86 var loader struct { 87 once sync.Once 88 events []*trace.Event 89 err error 90 } 91 92 func parseEvents() ([]*trace.Event, error) { 93 loader.once.Do(func() { 94 tracef, err := os.Open(flag.Arg(1)) 95 if err != nil { 96 loader.err = fmt.Errorf("failed to open trace file: %v", err) 97 return 98 } 99 defer tracef.Close() 100 101 // Parse and symbolize. 102 events, err := trace.Parse(bufio.NewReader(tracef)) 103 if err != nil { 104 loader.err = fmt.Errorf("failed to parse trace: %v", err) 105 return 106 } 107 err = trace.Symbolize(events, programBinary) 108 if err != nil { 109 loader.err = fmt.Errorf("failed to symbolize trace: %v", err) 110 return 111 } 112 loader.events = events 113 }) 114 return loader.events, loader.err 115 } 116 117 // httpMain serves the starting page. 118 func httpMain(w http.ResponseWriter, r *http.Request) { 119 w.Write(templMain) 120 } 121 122 var templMain = []byte(` 123 <html> 124 <body> 125 <a href="/trace">View trace</a><br> 126 <a href="/goroutines">Goroutine analysis</a><br> 127 <a href="/io">Network blocking profile</a><br> 128 <a href="/block">Synchronization blocking profile</a><br> 129 <a href="/syscall">Syscall blocking profile</a><br> 130 <a href="/sched">Scheduler latency profile</a><br> 131 </body> 132 </html> 133 `) 134 135 // startBrowser tries to open the URL in a browser 136 // and reports whether it succeeds. 137 // Note: copied from x/tools/cmd/cover/html.go 138 func startBrowser(url string) bool { 139 // try to start the browser 140 var args []string 141 switch runtime.GOOS { 142 case "darwin": 143 args = []string{"open"} 144 case "windows": 145 args = []string{"cmd", "/c", "start"} 146 default: 147 args = []string{"xdg-open"} 148 } 149 cmd := exec.Command(args[0], append(args[1:], url)...) 150 return cmd.Start() == nil 151 } 152 153 func dief(msg string, args ...interface{}) { 154 fmt.Fprintf(os.Stderr, msg, args...) 155 os.Exit(1) 156 } 157