|  | // Copyright 2014 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | /* | 
|  | Trace is a tool for viewing trace files. | 
|  |  | 
|  | Trace files can be generated with: | 
|  | - runtime/trace.Start | 
|  | - net/http/pprof package | 
|  | - go test -trace | 
|  |  | 
|  | Example usage: | 
|  | Generate a trace file with 'go test': | 
|  | go test -trace trace.out pkg | 
|  | View the trace in a web browser: | 
|  | go tool trace trace.out | 
|  | */ | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "bufio" | 
|  | "flag" | 
|  | "fmt" | 
|  | "html/template" | 
|  | "internal/trace" | 
|  | "log" | 
|  | "net" | 
|  | "net/http" | 
|  | "os" | 
|  | "os/exec" | 
|  | "runtime" | 
|  | "sync" | 
|  | ) | 
|  |  | 
|  | const usageMessage = "" + | 
|  | `Usage of 'go tool trace': | 
|  | Given a trace file produced by 'go test': | 
|  | go test -trace=trace.out pkg | 
|  |  | 
|  | Open a web browser displaying trace: | 
|  | go tool trace [flags] [pkg.test] trace.out | 
|  | [pkg.test] argument is required for traces produced by Go 1.6 and below. | 
|  | Go 1.7 does not require the binary argument. | 
|  |  | 
|  | Flags: | 
|  | -http=addr: HTTP service address (e.g., ':6060') | 
|  | ` | 
|  |  | 
|  | var ( | 
|  | httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')") | 
|  |  | 
|  | // The binary file name, left here for serveSVGProfile. | 
|  | programBinary string | 
|  | traceFile     string | 
|  | ) | 
|  |  | 
|  | func main() { | 
|  | flag.Usage = func() { | 
|  | fmt.Fprintln(os.Stderr, usageMessage) | 
|  | os.Exit(2) | 
|  | } | 
|  | flag.Parse() | 
|  |  | 
|  | // Go 1.7 traces embed symbol info and does not require the binary. | 
|  | // But we optionally accept binary as first arg for Go 1.5 traces. | 
|  | switch flag.NArg() { | 
|  | case 1: | 
|  | traceFile = flag.Arg(0) | 
|  | case 2: | 
|  | programBinary = flag.Arg(0) | 
|  | traceFile = flag.Arg(1) | 
|  | default: | 
|  | flag.Usage() | 
|  | } | 
|  |  | 
|  | ln, err := net.Listen("tcp", *httpFlag) | 
|  | if err != nil { | 
|  | dief("failed to create server socket: %v\n", err) | 
|  | } | 
|  |  | 
|  | log.Printf("Parsing trace...") | 
|  | events, err := parseEvents() | 
|  | if err != nil { | 
|  | dief("%v\n", err) | 
|  | } | 
|  |  | 
|  | log.Printf("Serializing trace...") | 
|  | params := &traceParams{ | 
|  | events:  events, | 
|  | endTime: int64(1<<63 - 1), | 
|  | } | 
|  | data := generateTrace(params) | 
|  |  | 
|  | log.Printf("Splitting trace...") | 
|  | ranges = splitTrace(data) | 
|  |  | 
|  | log.Printf("Opening browser") | 
|  | if !startBrowser("http://" + ln.Addr().String()) { | 
|  | fmt.Fprintf(os.Stderr, "Trace viewer is listening on http://%s\n", ln.Addr().String()) | 
|  | } | 
|  |  | 
|  | // Start http server. | 
|  | http.HandleFunc("/", httpMain) | 
|  | err = http.Serve(ln, nil) | 
|  | dief("failed to start http server: %v\n", err) | 
|  | } | 
|  |  | 
|  | var ranges []Range | 
|  |  | 
|  | var loader struct { | 
|  | once   sync.Once | 
|  | events []*trace.Event | 
|  | err    error | 
|  | } | 
|  |  | 
|  | func parseEvents() ([]*trace.Event, error) { | 
|  | loader.once.Do(func() { | 
|  | tracef, err := os.Open(traceFile) | 
|  | if err != nil { | 
|  | loader.err = fmt.Errorf("failed to open trace file: %v", err) | 
|  | return | 
|  | } | 
|  | defer tracef.Close() | 
|  |  | 
|  | // Parse and symbolize. | 
|  | events, err := trace.Parse(bufio.NewReader(tracef), programBinary) | 
|  | if err != nil { | 
|  | loader.err = fmt.Errorf("failed to parse trace: %v", err) | 
|  | return | 
|  | } | 
|  | loader.events = events | 
|  | }) | 
|  | return loader.events, loader.err | 
|  | } | 
|  |  | 
|  | // httpMain serves the starting page. | 
|  | func httpMain(w http.ResponseWriter, r *http.Request) { | 
|  | if err := templMain.Execute(w, ranges); err != nil { | 
|  | http.Error(w, err.Error(), http.StatusInternalServerError) | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | var templMain = template.Must(template.New("").Parse(` | 
|  | <html> | 
|  | <body> | 
|  | {{if $}} | 
|  | {{range $e := $}} | 
|  | <a href="/trace?start={{$e.Start}}&end={{$e.End}}">View trace ({{$e.Name}})</a><br> | 
|  | {{end}} | 
|  | <br> | 
|  | {{else}} | 
|  | <a href="/trace">View trace</a><br> | 
|  | {{end}} | 
|  | <a href="/goroutines">Goroutine analysis</a><br> | 
|  | <a href="/io">Network blocking profile</a><br> | 
|  | <a href="/block">Synchronization blocking profile</a><br> | 
|  | <a href="/syscall">Syscall blocking profile</a><br> | 
|  | <a href="/sched">Scheduler latency profile</a><br> | 
|  | </body> | 
|  | </html> | 
|  | `)) | 
|  |  | 
|  | // startBrowser tries to open the URL in a browser | 
|  | // and reports whether it succeeds. | 
|  | // Note: copied from x/tools/cmd/cover/html.go | 
|  | func startBrowser(url string) bool { | 
|  | // try to start the browser | 
|  | var args []string | 
|  | switch runtime.GOOS { | 
|  | case "darwin": | 
|  | args = []string{"open"} | 
|  | case "windows": | 
|  | args = []string{"cmd", "/c", "start"} | 
|  | default: | 
|  | args = []string{"xdg-open"} | 
|  | } | 
|  | cmd := exec.Command(args[0], append(args[1:], url)...) | 
|  | return cmd.Start() == nil | 
|  | } | 
|  |  | 
|  | func dief(msg string, args ...interface{}) { | 
|  | fmt.Fprintf(os.Stderr, msg, args...) | 
|  | os.Exit(1) | 
|  | } |