github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/trace/main.go (about)

     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 trace.out
    18  */
    19  package main
    20  
    21  import (
    22  	"bufio"
    23  	"flag"
    24  	"fmt"
    25  	"html/template"
    26  	"internal/trace"
    27  	"log"
    28  	"net"
    29  	"net/http"
    30  	"os"
    31  	"os/exec"
    32  	"runtime"
    33  	"sync"
    34  )
    35  
    36  const usageMessage = "" +
    37  	`Usage of 'go tool trace':
    38  Given a trace file produced by 'go test':
    39  	go test -trace=trace.out pkg
    40  
    41  Open a web browser displaying trace:
    42  	go tool trace [flags] [pkg.test] trace.out
    43  [pkg.test] argument is required for traces produced by Go 1.6 and below.
    44  Go 1.7 does not require the binary argument.
    45  
    46  Flags:
    47  	-http=addr: HTTP service address (e.g., ':6060')
    48  `
    49  
    50  var (
    51  	httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')")
    52  
    53  	// The binary file name, left here for serveSVGProfile.
    54  	programBinary string
    55  	traceFile     string
    56  )
    57  
    58  func main() {
    59  	flag.Usage = func() {
    60  		fmt.Fprintln(os.Stderr, usageMessage)
    61  		os.Exit(2)
    62  	}
    63  	flag.Parse()
    64  
    65  	// Go 1.7 traces embed symbol info and does not require the binary.
    66  	// But we optionally accept binary as first arg for Go 1.5 traces.
    67  	switch flag.NArg() {
    68  	case 1:
    69  		traceFile = flag.Arg(0)
    70  	case 2:
    71  		programBinary = flag.Arg(0)
    72  		traceFile = flag.Arg(1)
    73  	default:
    74  		flag.Usage()
    75  	}
    76  
    77  	ln, err := net.Listen("tcp", *httpFlag)
    78  	if err != nil {
    79  		dief("failed to create server socket: %v\n", err)
    80  	}
    81  
    82  	log.Printf("Parsing trace...")
    83  	events, err := parseEvents()
    84  	if err != nil {
    85  		dief("%v\n", err)
    86  	}
    87  
    88  	log.Printf("Serializing trace...")
    89  	params := &traceParams{
    90  		events:  events,
    91  		endTime: int64(1<<63 - 1),
    92  	}
    93  	data := generateTrace(params)
    94  
    95  	log.Printf("Splitting trace...")
    96  	ranges = splitTrace(data)
    97  
    98  	log.Printf("Opening browser")
    99  	if !startBrowser("http://" + ln.Addr().String()) {
   100  		fmt.Fprintf(os.Stderr, "Trace viewer is listening on http://%s\n", ln.Addr().String())
   101  	}
   102  
   103  	// Start http server.
   104  	http.HandleFunc("/", httpMain)
   105  	err = http.Serve(ln, nil)
   106  	dief("failed to start http server: %v\n", err)
   107  }
   108  
   109  var ranges []Range
   110  
   111  var loader struct {
   112  	once   sync.Once
   113  	events []*trace.Event
   114  	err    error
   115  }
   116  
   117  func parseEvents() ([]*trace.Event, error) {
   118  	loader.once.Do(func() {
   119  		tracef, err := os.Open(traceFile)
   120  		if err != nil {
   121  			loader.err = fmt.Errorf("failed to open trace file: %v", err)
   122  			return
   123  		}
   124  		defer tracef.Close()
   125  
   126  		// Parse and symbolize.
   127  		events, err := trace.Parse(bufio.NewReader(tracef), programBinary)
   128  		if err != nil {
   129  			loader.err = fmt.Errorf("failed to parse trace: %v", err)
   130  			return
   131  		}
   132  		loader.events = events
   133  	})
   134  	return loader.events, loader.err
   135  }
   136  
   137  // httpMain serves the starting page.
   138  func httpMain(w http.ResponseWriter, r *http.Request) {
   139  	if err := templMain.Execute(w, ranges); err != nil {
   140  		http.Error(w, err.Error(), http.StatusInternalServerError)
   141  		return
   142  	}
   143  }
   144  
   145  var templMain = template.Must(template.New("").Parse(`
   146  <html>
   147  <body>
   148  {{if $}}
   149  	{{range $e := $}}
   150  		<a href="/trace?start={{$e.Start}}&end={{$e.End}}">View trace ({{$e.Name}})</a><br>
   151  	{{end}}
   152  	<br>
   153  {{else}}
   154  	<a href="/trace">View trace</a><br>
   155  {{end}}
   156  <a href="/goroutines">Goroutine analysis</a><br>
   157  <a href="/io">Network blocking profile</a><br>
   158  <a href="/block">Synchronization blocking profile</a><br>
   159  <a href="/syscall">Syscall blocking profile</a><br>
   160  <a href="/sched">Scheduler latency profile</a><br>
   161  </body>
   162  </html>
   163  `))
   164  
   165  // startBrowser tries to open the URL in a browser
   166  // and reports whether it succeeds.
   167  // Note: copied from x/tools/cmd/cover/html.go
   168  func startBrowser(url string) bool {
   169  	// try to start the browser
   170  	var args []string
   171  	switch runtime.GOOS {
   172  	case "darwin":
   173  		args = []string{"open"}
   174  	case "windows":
   175  		args = []string{"cmd", "/c", "start"}
   176  	default:
   177  		args = []string{"xdg-open"}
   178  	}
   179  	cmd := exec.Command(args[0], append(args[1:], url)...)
   180  	return cmd.Start() == nil
   181  }
   182  
   183  func dief(msg string, args ...interface{}) {
   184  	fmt.Fprintf(os.Stderr, msg, args...)
   185  	os.Exit(1)
   186  }