blob: c7bf75ef689057e60c0619386da97a880a521abd [file] [log] [blame] [edit]
// 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
Generate a pprof-like profile from the trace:
go tool trace -pprof=TYPE trace.out > TYPE.pprof
Supported profile types are:
- net: network blocking profile
- sync: synchronization blocking profile
- syscall: syscall blocking profile
- sched: scheduler latency profile
*/
package main
import (
"bufio"
"cmd/internal/browser"
"flag"
"fmt"
"html/template"
"internal/trace"
"io"
"log"
"net"
"net/http"
"os"
"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
Generate a pprof-like profile from the trace:
go tool trace -pprof=TYPE [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.
Supported profile types are:
- net: network blocking profile
- sync: synchronization blocking profile
- syscall: syscall blocking profile
- sched: scheduler latency profile
Flags:
-http=addr: HTTP service address (e.g., ':6060')
-pprof=type: print a pprof-like profile instead
`
var (
httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')")
pprofFlag = flag.String("pprof", "", "print a pprof-like profile instead")
// 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()
}
var pprofFunc func(io.Writer) error
switch *pprofFlag {
case "net":
pprofFunc = pprofIO
case "sync":
pprofFunc = pprofBlock
case "syscall":
pprofFunc = pprofSyscall
case "sched":
pprofFunc = pprofSched
}
if pprofFunc != nil {
if err := pprofFunc(os.Stdout); err != nil {
dief("failed to generate pprof: %v\n", err)
}
os.Exit(0)
}
if *pprofFlag != "" {
dief("unknown pprof type %s\n", *pprofFlag)
}
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, err := generateTrace(params)
if err != nil {
dief("%v\n", err)
}
log.Printf("Splitting trace...")
ranges = splitTrace(data)
log.Printf("Opening browser")
if !browser.Open("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>
`))
func dief(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg, args...)
os.Exit(1)
}