Source file src/cmd/trace/v2/main.go

     1  // Copyright 2023 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 trace
     6  
     7  import (
     8  	"fmt"
     9  	"internal/trace"
    10  	"internal/trace/traceviewer"
    11  	tracev2 "internal/trace/v2"
    12  	"io"
    13  	"log"
    14  	"net"
    15  	"net/http"
    16  	"os"
    17  
    18  	"internal/trace/v2/raw"
    19  
    20  	"cmd/internal/browser"
    21  )
    22  
    23  // Main is the main function for cmd/trace v2.
    24  func Main(traceFile, httpAddr, pprof string, debug int) error {
    25  	tracef, err := os.Open(traceFile)
    26  	if err != nil {
    27  		return fmt.Errorf("failed to read trace file: %w", err)
    28  	}
    29  	defer tracef.Close()
    30  
    31  	// Debug flags.
    32  	switch debug {
    33  	case 1:
    34  		return debugProcessedEvents(tracef)
    35  	case 2:
    36  		return debugRawEvents(tracef)
    37  	}
    38  
    39  	ln, err := net.Listen("tcp", httpAddr)
    40  	if err != nil {
    41  		return fmt.Errorf("failed to create server socket: %w", err)
    42  	}
    43  	addr := "http://" + ln.Addr().String()
    44  
    45  	log.Print("Preparing trace for viewer...")
    46  	parsed, err := parseTrace(tracef)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	// N.B. tracef not needed after this point.
    51  	// We might double-close, but that's fine; we ignore the error.
    52  	tracef.Close()
    53  
    54  	log.Print("Splitting trace for viewer...")
    55  	ranges, err := splitTrace(parsed)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	log.Printf("Opening browser. Trace viewer is listening on %s", addr)
    61  	browser.Open(addr)
    62  
    63  	mutatorUtil := func(flags trace.UtilFlags) ([][]trace.MutatorUtil, error) {
    64  		return trace.MutatorUtilizationV2(parsed.events, flags), nil
    65  	}
    66  
    67  	mux := http.NewServeMux()
    68  
    69  	// Main endpoint.
    70  	mux.Handle("/", traceviewer.MainHandler([]traceviewer.View{
    71  		{Type: traceviewer.ViewProc, Ranges: ranges},
    72  		// N.B. Use the same ranges for threads. It takes a long time to compute
    73  		// the split a second time, but the makeup of the events are similar enough
    74  		// that this is still a good split.
    75  		{Type: traceviewer.ViewThread, Ranges: ranges},
    76  	}))
    77  
    78  	// Catapult handlers.
    79  	mux.Handle("/trace", traceviewer.TraceHandler())
    80  	mux.Handle("/jsontrace", JSONTraceHandler(parsed))
    81  	mux.Handle("/static/", traceviewer.StaticHandler())
    82  
    83  	// Goroutines handlers.
    84  	mux.HandleFunc("/goroutines", GoroutinesHandlerFunc(parsed.summary.Goroutines))
    85  	mux.HandleFunc("/goroutine", GoroutineHandler(parsed.summary.Goroutines))
    86  
    87  	// MMU handler.
    88  	mux.HandleFunc("/mmu", traceviewer.MMUHandlerFunc(ranges, mutatorUtil))
    89  
    90  	// Basic pprof endpoints.
    91  	mux.HandleFunc("/io", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofIO(), parsed)))
    92  	mux.HandleFunc("/block", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofBlock(), parsed)))
    93  	mux.HandleFunc("/syscall", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSyscall(), parsed)))
    94  	mux.HandleFunc("/sched", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSched(), parsed)))
    95  
    96  	// Region-based pprof endpoints.
    97  	mux.HandleFunc("/regionio", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofIO(), parsed)))
    98  	mux.HandleFunc("/regionblock", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofBlock(), parsed)))
    99  	mux.HandleFunc("/regionsyscall", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSyscall(), parsed)))
   100  	mux.HandleFunc("/regionsched", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSched(), parsed)))
   101  
   102  	// Region endpoints.
   103  	mux.HandleFunc("/userregions", UserRegionsHandlerFunc(parsed))
   104  	mux.HandleFunc("/userregion", UserRegionHandlerFunc(parsed))
   105  
   106  	// Task endpoints.
   107  	mux.HandleFunc("/usertasks", UserTasksHandlerFunc(parsed))
   108  	mux.HandleFunc("/usertask", UserTaskHandlerFunc(parsed))
   109  
   110  	err = http.Serve(ln, mux)
   111  	return fmt.Errorf("failed to start http server: %w", err)
   112  }
   113  
   114  type parsedTrace struct {
   115  	events  []tracev2.Event
   116  	summary *trace.Summary
   117  }
   118  
   119  func parseTrace(tr io.Reader) (*parsedTrace, error) {
   120  	r, err := tracev2.NewReader(tr)
   121  	if err != nil {
   122  		return nil, fmt.Errorf("failed to create trace reader: %w", err)
   123  	}
   124  	s := trace.NewSummarizer()
   125  	t := new(parsedTrace)
   126  	for {
   127  		ev, err := r.ReadEvent()
   128  		if err == io.EOF {
   129  			break
   130  		} else if err != nil {
   131  			return nil, fmt.Errorf("failed to read event: %w", err)
   132  		}
   133  		t.events = append(t.events, ev)
   134  		s.Event(&t.events[len(t.events)-1])
   135  	}
   136  	t.summary = s.Finalize()
   137  	return t, nil
   138  }
   139  
   140  func (t *parsedTrace) startTime() tracev2.Time {
   141  	return t.events[0].Time()
   142  }
   143  
   144  func (t *parsedTrace) endTime() tracev2.Time {
   145  	return t.events[len(t.events)-1].Time()
   146  }
   147  
   148  // splitTrace splits the trace into a number of ranges, each resulting in approx 100 MiB of
   149  // json output (the trace viewer can hardly handle more).
   150  func splitTrace(parsed *parsedTrace) ([]traceviewer.Range, error) {
   151  	// TODO(mknyszek): Split traces by generation by doing a quick first pass over the
   152  	// trace to identify all the generation boundaries.
   153  	s, c := traceviewer.SplittingTraceConsumer(100 << 20) // 100 MiB
   154  	if err := generateTrace(parsed, defaultGenOpts(), c); err != nil {
   155  		return nil, err
   156  	}
   157  	return s.Ranges, nil
   158  }
   159  
   160  func debugProcessedEvents(trace io.Reader) error {
   161  	tr, err := tracev2.NewReader(trace)
   162  	if err != nil {
   163  		return err
   164  	}
   165  	for {
   166  		ev, err := tr.ReadEvent()
   167  		if err == io.EOF {
   168  			return nil
   169  		} else if err != nil {
   170  			return err
   171  		}
   172  		fmt.Println(ev.String())
   173  	}
   174  }
   175  
   176  func debugRawEvents(trace io.Reader) error {
   177  	rr, err := raw.NewReader(trace)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	for {
   182  		ev, err := rr.ReadEvent()
   183  		if err == io.EOF {
   184  			return nil
   185  		} else if err != nil {
   186  			return err
   187  		}
   188  		fmt.Println(ev.String())
   189  	}
   190  }
   191  

View as plain text