...

Source file src/cmd/trace/main.go

     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	package main
     6	
     7	import (
     8		"bufio"
     9		"cmd/internal/browser"
    10		"flag"
    11		"fmt"
    12		"html/template"
    13		"internal/trace"
    14		"io"
    15		"log"
    16		"net"
    17		"net/http"
    18		"os"
    19		"runtime"
    20		"runtime/debug"
    21		"sync"
    22	
    23		_ "net/http/pprof" // Required to use pprof
    24	)
    25	
    26	const usageMessage = "" +
    27		`Usage of 'go tool trace':
    28	Given a trace file produced by 'go test':
    29		go test -trace=trace.out pkg
    30	
    31	Open a web browser displaying trace:
    32		go tool trace [flags] [pkg.test] trace.out
    33	
    34	Generate a pprof-like profile from the trace:
    35	    go tool trace -pprof=TYPE [pkg.test] trace.out
    36	
    37	[pkg.test] argument is required for traces produced by Go 1.6 and below.
    38	Go 1.7 does not require the binary argument.
    39	
    40	Supported profile types are:
    41	    - net: network blocking profile
    42	    - sync: synchronization blocking profile
    43	    - syscall: syscall blocking profile
    44	    - sched: scheduler latency profile
    45	
    46	Flags:
    47		-http=addr: HTTP service address (e.g., ':6060')
    48		-pprof=type: print a pprof-like profile instead
    49		-d: print debug info such as parsed events
    50	
    51	Note that while the various profiles available when launching
    52	'go tool trace' work on every browser, the trace viewer itself
    53	(the 'view trace' page) comes from the Chrome/Chromium project
    54	and is only actively tested on that browser.
    55	`
    56	
    57	var (
    58		httpFlag  = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')")
    59		pprofFlag = flag.String("pprof", "", "print a pprof-like profile instead")
    60		debugFlag = flag.Bool("d", false, "print debug information such as parsed events list")
    61	
    62		// The binary file name, left here for serveSVGProfile.
    63		programBinary string
    64		traceFile     string
    65	)
    66	
    67	func main() {
    68		flag.Usage = func() {
    69			fmt.Fprintln(os.Stderr, usageMessage)
    70			os.Exit(2)
    71		}
    72		flag.Parse()
    73	
    74		// Go 1.7 traces embed symbol info and does not require the binary.
    75		// But we optionally accept binary as first arg for Go 1.5 traces.
    76		switch flag.NArg() {
    77		case 1:
    78			traceFile = flag.Arg(0)
    79		case 2:
    80			programBinary = flag.Arg(0)
    81			traceFile = flag.Arg(1)
    82		default:
    83			flag.Usage()
    84		}
    85	
    86		var pprofFunc func(io.Writer, *http.Request) error
    87		switch *pprofFlag {
    88		case "net":
    89			pprofFunc = pprofByGoroutine(computePprofIO)
    90		case "sync":
    91			pprofFunc = pprofByGoroutine(computePprofBlock)
    92		case "syscall":
    93			pprofFunc = pprofByGoroutine(computePprofSyscall)
    94		case "sched":
    95			pprofFunc = pprofByGoroutine(computePprofSched)
    96		}
    97		if pprofFunc != nil {
    98			if err := pprofFunc(os.Stdout, &http.Request{}); err != nil {
    99				dief("failed to generate pprof: %v\n", err)
   100			}
   101			os.Exit(0)
   102		}
   103		if *pprofFlag != "" {
   104			dief("unknown pprof type %s\n", *pprofFlag)
   105		}
   106	
   107		ln, err := net.Listen("tcp", *httpFlag)
   108		if err != nil {
   109			dief("failed to create server socket: %v\n", err)
   110		}
   111	
   112		log.Print("Parsing trace...")
   113		res, err := parseTrace()
   114		if err != nil {
   115			dief("%v\n", err)
   116		}
   117	
   118		if *debugFlag {
   119			trace.Print(res.Events)
   120			os.Exit(0)
   121		}
   122		reportMemoryUsage("after parsing trace")
   123		debug.FreeOSMemory()
   124	
   125		log.Print("Splitting trace...")
   126		ranges = splitTrace(res)
   127		reportMemoryUsage("after spliting trace")
   128		debug.FreeOSMemory()
   129	
   130		addr := "http://" + ln.Addr().String()
   131		log.Printf("Opening browser. Trace viewer is listening on %s", addr)
   132		browser.Open(addr)
   133	
   134		// Start http server.
   135		http.HandleFunc("/", httpMain)
   136		err = http.Serve(ln, nil)
   137		dief("failed to start http server: %v\n", err)
   138	}
   139	
   140	var ranges []Range
   141	
   142	var loader struct {
   143		once sync.Once
   144		res  trace.ParseResult
   145		err  error
   146	}
   147	
   148	// parseEvents is a compatibility wrapper that returns only
   149	// the Events part of trace.ParseResult returned by parseTrace.
   150	func parseEvents() ([]*trace.Event, error) {
   151		res, err := parseTrace()
   152		if err != nil {
   153			return nil, err
   154		}
   155		return res.Events, err
   156	}
   157	
   158	func parseTrace() (trace.ParseResult, error) {
   159		loader.once.Do(func() {
   160			tracef, err := os.Open(traceFile)
   161			if err != nil {
   162				loader.err = fmt.Errorf("failed to open trace file: %v", err)
   163				return
   164			}
   165			defer tracef.Close()
   166	
   167			// Parse and symbolize.
   168			res, err := trace.Parse(bufio.NewReader(tracef), programBinary)
   169			if err != nil {
   170				loader.err = fmt.Errorf("failed to parse trace: %v", err)
   171				return
   172			}
   173			loader.res = res
   174		})
   175		return loader.res, loader.err
   176	}
   177	
   178	// httpMain serves the starting page.
   179	func httpMain(w http.ResponseWriter, r *http.Request) {
   180		if err := templMain.Execute(w, ranges); err != nil {
   181			http.Error(w, err.Error(), http.StatusInternalServerError)
   182			return
   183		}
   184	}
   185	
   186	var templMain = template.Must(template.New("").Parse(`
   187	<html>
   188	<body>
   189	{{if $}}
   190		{{range $e := $}}
   191			<a href="{{$e.URL}}">View trace ({{$e.Name}})</a><br>
   192		{{end}}
   193		<br>
   194	{{else}}
   195		<a href="/trace">View trace</a><br>
   196	{{end}}
   197	<a href="/goroutines">Goroutine analysis</a><br>
   198	<a href="/io">Network blocking profile</a> (<a href="/io?raw=1" download="io.profile">⬇</a>)<br>
   199	<a href="/block">Synchronization blocking profile</a> (<a href="/block?raw=1" download="block.profile">⬇</a>)<br>
   200	<a href="/syscall">Syscall blocking profile</a> (<a href="/syscall?raw=1" download="syscall.profile">⬇</a>)<br>
   201	<a href="/sched">Scheduler latency profile</a> (<a href="/sche?raw=1" download="sched.profile">⬇</a>)<br>
   202	<a href="/usertasks">User-defined tasks</a><br>
   203	<a href="/userregions">User-defined regions</a><br>
   204	<a href="/mmu">Minimum mutator utilization</a><br>
   205	</body>
   206	</html>
   207	`))
   208	
   209	func dief(msg string, args ...interface{}) {
   210		fmt.Fprintf(os.Stderr, msg, args...)
   211		os.Exit(1)
   212	}
   213	
   214	var debugMemoryUsage bool
   215	
   216	func init() {
   217		v := os.Getenv("DEBUG_MEMORY_USAGE")
   218		debugMemoryUsage = v != ""
   219	}
   220	
   221	func reportMemoryUsage(msg string) {
   222		if !debugMemoryUsage {
   223			return
   224		}
   225		var s runtime.MemStats
   226		runtime.ReadMemStats(&s)
   227		w := os.Stderr
   228		fmt.Fprintf(w, "%s\n", msg)
   229		fmt.Fprintf(w, " Alloc:\t%d Bytes\n", s.Alloc)
   230		fmt.Fprintf(w, " Sys:\t%d Bytes\n", s.Sys)
   231		fmt.Fprintf(w, " HeapReleased:\t%d Bytes\n", s.HeapReleased)
   232		fmt.Fprintf(w, " HeapSys:\t%d Bytes\n", s.HeapSys)
   233		fmt.Fprintf(w, " HeapInUse:\t%d Bytes\n", s.HeapInuse)
   234		fmt.Fprintf(w, " HeapAlloc:\t%d Bytes\n", s.HeapAlloc)
   235		var dummy string
   236		fmt.Printf("Enter to continue...")
   237		fmt.Scanf("%s", &dummy)
   238	}
   239	

View as plain text