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  	cmdv2 "cmd/trace/v2"
    11  	"flag"
    12  	"fmt"
    13  	"internal/trace"
    14  	"internal/trace/traceviewer"
    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=int: print debug info such as parsed events (1 for high-level, 2 for low-level)
    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.Int("d", 0, "print debug information (1 for basic debug info, 2 for lower-level info)")
    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  	if isTraceV2(traceFile) {
    87  		if err := cmdv2.Main(traceFile, *httpFlag, *pprofFlag, *debugFlag); err != nil {
    88  			dief("%s\n", err)
    89  		}
    90  		return
    91  	}
    92  
    93  	var pprofFunc traceviewer.ProfileFunc
    94  	switch *pprofFlag {
    95  	case "net":
    96  		pprofFunc = pprofByGoroutine(computePprofIO)
    97  	case "sync":
    98  		pprofFunc = pprofByGoroutine(computePprofBlock)
    99  	case "syscall":
   100  		pprofFunc = pprofByGoroutine(computePprofSyscall)
   101  	case "sched":
   102  		pprofFunc = pprofByGoroutine(computePprofSched)
   103  	}
   104  	if pprofFunc != nil {
   105  		records, err := pprofFunc(&http.Request{})
   106  		if err != nil {
   107  			dief("failed to generate pprof: %v\n", err)
   108  		}
   109  		if err := traceviewer.BuildProfile(records).Write(os.Stdout); err != nil {
   110  			dief("failed to generate pprof: %v\n", err)
   111  		}
   112  		os.Exit(0)
   113  	}
   114  	if *pprofFlag != "" {
   115  		dief("unknown pprof type %s\n", *pprofFlag)
   116  	}
   117  
   118  	ln, err := net.Listen("tcp", *httpFlag)
   119  	if err != nil {
   120  		dief("failed to create server socket: %v\n", err)
   121  	}
   122  
   123  	log.Print("Parsing trace...")
   124  	res, err := parseTrace()
   125  	if err != nil {
   126  		dief("%v\n", err)
   127  	}
   128  
   129  	if *debugFlag != 0 {
   130  		trace.Print(res.Events)
   131  		os.Exit(0)
   132  	}
   133  	reportMemoryUsage("after parsing trace")
   134  	debug.FreeOSMemory()
   135  
   136  	log.Print("Splitting trace...")
   137  	ranges = splitTrace(res)
   138  	reportMemoryUsage("after splitting trace")
   139  	debug.FreeOSMemory()
   140  
   141  	addr := "http://" + ln.Addr().String()
   142  	log.Printf("Opening browser. Trace viewer is listening on %s", addr)
   143  	browser.Open(addr)
   144  
   145  	// Install MMU handler.
   146  	http.HandleFunc("/mmu", traceviewer.MMUHandlerFunc(ranges, mutatorUtil))
   147  
   148  	// Install main handler.
   149  	http.Handle("/", traceviewer.MainHandler([]traceviewer.View{
   150  		{Type: traceviewer.ViewProc, Ranges: ranges},
   151  	}))
   152  
   153  	// Start http server.
   154  	err = http.Serve(ln, nil)
   155  	dief("failed to start http server: %v\n", err)
   156  }
   157  
   158  // isTraceV2 returns true if filename holds a v2 trace.
   159  func isTraceV2(filename string) bool {
   160  	file, err := os.Open(filename)
   161  	if err != nil {
   162  		return false
   163  	}
   164  	defer file.Close()
   165  
   166  	ver, _, err := trace.ReadVersion(file)
   167  	if err != nil {
   168  		return false
   169  	}
   170  	return ver >= 1022
   171  }
   172  
   173  var ranges []traceviewer.Range
   174  
   175  var loader struct {
   176  	once sync.Once
   177  	res  trace.ParseResult
   178  	err  error
   179  }
   180  
   181  // parseEvents is a compatibility wrapper that returns only
   182  // the Events part of trace.ParseResult returned by parseTrace.
   183  func parseEvents() ([]*trace.Event, error) {
   184  	res, err := parseTrace()
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  	return res.Events, err
   189  }
   190  
   191  func parseTrace() (trace.ParseResult, error) {
   192  	loader.once.Do(func() {
   193  		tracef, err := os.Open(traceFile)
   194  		if err != nil {
   195  			loader.err = fmt.Errorf("failed to open trace file: %v", err)
   196  			return
   197  		}
   198  		defer tracef.Close()
   199  
   200  		// Parse and symbolize.
   201  		res, err := trace.Parse(bufio.NewReader(tracef), programBinary)
   202  		if err != nil {
   203  			loader.err = fmt.Errorf("failed to parse trace: %v", err)
   204  			return
   205  		}
   206  		loader.res = res
   207  	})
   208  	return loader.res, loader.err
   209  }
   210  
   211  func dief(msg string, args ...any) {
   212  	fmt.Fprintf(os.Stderr, msg, args...)
   213  	os.Exit(1)
   214  }
   215  
   216  var debugMemoryUsage bool
   217  
   218  func init() {
   219  	v := os.Getenv("DEBUG_MEMORY_USAGE")
   220  	debugMemoryUsage = v != ""
   221  }
   222  
   223  func reportMemoryUsage(msg string) {
   224  	if !debugMemoryUsage {
   225  		return
   226  	}
   227  	var s runtime.MemStats
   228  	runtime.ReadMemStats(&s)
   229  	w := os.Stderr
   230  	fmt.Fprintf(w, "%s\n", msg)
   231  	fmt.Fprintf(w, " Alloc:\t%d Bytes\n", s.Alloc)
   232  	fmt.Fprintf(w, " Sys:\t%d Bytes\n", s.Sys)
   233  	fmt.Fprintf(w, " HeapReleased:\t%d Bytes\n", s.HeapReleased)
   234  	fmt.Fprintf(w, " HeapSys:\t%d Bytes\n", s.HeapSys)
   235  	fmt.Fprintf(w, " HeapInUse:\t%d Bytes\n", s.HeapInuse)
   236  	fmt.Fprintf(w, " HeapAlloc:\t%d Bytes\n", s.HeapAlloc)
   237  	var dummy string
   238  	fmt.Printf("Enter to continue...")
   239  	fmt.Scanf("%s", &dummy)
   240  }
   241  
   242  func mutatorUtil(flags trace.UtilFlags) ([][]trace.MutatorUtil, error) {
   243  	events, err := parseEvents()
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	return trace.MutatorUtilization(events, flags), nil
   248  }
   249  

View as plain text