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.Fprint(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 ...any) {
   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