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"
|
2016-09-14 12:16:50 -06:00
|
|
|
"cmd/internal/browser"
|
2015-01-30 03:31:43 -07:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
2016-05-03 08:44:25 -06:00
|
|
|
"html/template"
|
2015-01-30 03:31:43 -07:00
|
|
|
"internal/trace"
|
2016-05-03 08:44:25 -06:00
|
|
|
"log"
|
2015-01-30 03:31:43 -07:00
|
|
|
"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:
|
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)
|
|
|
|
}
|
2016-05-03 08:44:25 -06:00
|
|
|
|
|
|
|
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),
|
|
|
|
}
|
2016-08-08 15:24:07 -06:00
|
|
|
data, err := generateTrace(params)
|
|
|
|
if err != nil {
|
|
|
|
dief("%v\n", err)
|
|
|
|
}
|
2016-05-03 08:44:25 -06:00
|
|
|
|
|
|
|
log.Printf("Splitting trace...")
|
|
|
|
ranges = splitTrace(data)
|
|
|
|
|
|
|
|
log.Printf("Opening browser")
|
2016-09-14 12:16:50 -06:00
|
|
|
if !browser.Open("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
|
|
|
}
|
|
|
|
|
|
|
|
// Start http server.
|
|
|
|
http.HandleFunc("/", httpMain)
|
|
|
|
err = http.Serve(ln, nil)
|
|
|
|
dief("failed to start http server: %v\n", err)
|
|
|
|
}
|
|
|
|
|
2016-05-03 08:44:25 -06:00
|
|
|
var ranges []Range
|
|
|
|
|
2015-01-30 03:31:43 -07:00
|
|
|
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) {
|
2016-05-03 08:44:25 -06:00
|
|
|
if err := templMain.Execute(w, ranges); err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2015-01-30 03:31:43 -07:00
|
|
|
}
|
|
|
|
|
2016-05-03 08:44:25 -06:00
|
|
|
var templMain = template.Must(template.New("").Parse(`
|
2015-01-30 03:31:43 -07:00
|
|
|
<html>
|
|
|
|
<body>
|
2016-05-03 08:44:25 -06:00
|
|
|
{{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}}
|
2015-01-30 03:31:43 -07:00
|
|
|
<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>
|
2016-05-03 08:44:25 -06:00
|
|
|
`))
|
2015-01-30 03:31:43 -07:00
|
|
|
|
|
|
|
func dief(msg string, args ...interface{}) {
|
|
|
|
fmt.Fprintf(os.Stderr, msg, args...)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|