aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/trace/main.go
blob: ea6eef353f19a083d1384cfc8203624c9177b981 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 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/pprof.StartTrace
	- 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 pkg.test trace.out
*/
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:
	go tool trace [flags] pkg.test trace.out

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()

	// Usage information when no arguments.
	if flag.NArg() != 2 {
		flag.Usage()
	}
	programBinary = flag.Arg(0)
	traceFile = flag.Arg(1)

	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()) {
		dief("failed to start browser\n")
	}

	// 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() {
		tracef, err := os.Open(flag.Arg(1))
		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))
		if err != nil {
			loader.err = fmt.Errorf("failed to parse trace: %v", err)
			return
		}
		err = trace.Symbolize(events, programBinary)
		if err != nil {
			loader.err = fmt.Errorf("failed to symbolize 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>
<a href="/io">IO 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)
}