2015-01-30 03:31:43 -07:00
|
|
|
// 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:
|
2015-07-22 05:09:26 -06:00
|
|
|
- runtime/trace.Start
|
2015-01-30 03:31:43 -07:00
|
|
|
- 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:
|
2016-04-24 05:33:33 -06:00
|
|
|
go tool trace trace.out
|
2015-01-30 03:31:43 -07:00
|
|
|
*/
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"internal/trace"
|
|
|
|
"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:
|
2016-04-24 05:33:33 -06:00
|
|
|
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.
|
2015-01-30 03:31:43 -07:00
|
|
|
|
|
|
|
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()
|
|
|
|
|
2016-04-24 05:33:33 -06:00
|
|
|
// 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:
|
2015-01-30 03:31:43 -07:00
|
|
|
flag.Usage()
|
|
|
|
}
|
|
|
|
|
|
|
|
ln, err := net.Listen("tcp", *httpFlag)
|
|
|
|
if err != nil {
|
|
|
|
dief("failed to create server socket: %v\n", err)
|
|
|
|
}
|
|
|
|
// Open browser.
|
|
|
|
if !startBrowser("http://" + ln.Addr().String()) {
|
2015-08-21 12:31:50 -06:00
|
|
|
fmt.Fprintf(os.Stderr, "Trace viewer is listening on http://%s\n", ln.Addr().String())
|
2015-01-30 03:31:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse and symbolize trace asynchronously while browser opens.
|
|
|
|
go parseEvents()
|
|
|
|
|
|
|
|
// Start http server.
|
|
|
|
http.HandleFunc("/", httpMain)
|
|
|
|
err = http.Serve(ln, nil)
|
|
|
|
dief("failed to start http server: %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var loader struct {
|
|
|
|
once sync.Once
|
|
|
|
events []*trace.Event
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseEvents() ([]*trace.Event, error) {
|
|
|
|
loader.once.Do(func() {
|
2016-04-24 05:33:33 -06:00
|
|
|
tracef, err := os.Open(traceFile)
|
2015-01-30 03:31:43 -07:00
|
|
|
if err != nil {
|
|
|
|
loader.err = fmt.Errorf("failed to open trace file: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer tracef.Close()
|
|
|
|
|
|
|
|
// Parse and symbolize.
|
2016-04-11 00:57:52 -06:00
|
|
|
events, err := trace.Parse(bufio.NewReader(tracef), programBinary)
|
2015-01-30 03:31:43 -07:00
|
|
|
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) {
|
|
|
|
w.Write(templMain)
|
|
|
|
}
|
|
|
|
|
|
|
|
var templMain = []byte(`
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<a href="/trace">View trace</a><br>
|
|
|
|
<a href="/goroutines">Goroutine analysis</a><br>
|
2015-06-18 08:19:18 -06:00
|
|
|
<a href="/io">Network blocking profile</a><br>
|
2015-01-30 03:31:43 -07:00
|
|
|
<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)
|
|
|
|
}
|