github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/src/cmd/trace/trace.go (about)

     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  	"encoding/json"
     9  	"fmt"
    10  	"internal/trace"
    11  	"log"
    12  	"net/http"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  )
    19  
    20  func init() {
    21  	http.HandleFunc("/trace", httpTrace)
    22  	http.HandleFunc("/jsontrace", httpJsonTrace)
    23  	http.HandleFunc("/trace_viewer_html", httpTraceViewerHTML)
    24  }
    25  
    26  // httpTrace serves either whole trace (goid==0) or trace for goid goroutine.
    27  func httpTrace(w http.ResponseWriter, r *http.Request) {
    28  	_, err := parseTrace()
    29  	if err != nil {
    30  		http.Error(w, err.Error(), http.StatusInternalServerError)
    31  		return
    32  	}
    33  	if err := r.ParseForm(); err != nil {
    34  		http.Error(w, err.Error(), http.StatusInternalServerError)
    35  		return
    36  	}
    37  	html := strings.Replace(templTrace, "{{PARAMS}}", r.Form.Encode(), -1)
    38  	w.Write([]byte(html))
    39  
    40  }
    41  
    42  // See https://github.com/catapult-project/catapult/blob/master/tracing/docs/embedding-trace-viewer.md
    43  // This is almost verbatim copy of:
    44  // https://github.com/catapult-project/catapult/blob/master/tracing/bin/index.html
    45  // on revision 5f9e4c3eaa555bdef18218a89f38c768303b7b6e.
    46  var templTrace = `
    47  <html>
    48  <head>
    49  <link href="/trace_viewer_html" rel="import">
    50  <style type="text/css">
    51    html, body {
    52      box-sizing: border-box;
    53      overflow: hidden;
    54      margin: 0px;
    55      padding: 0;
    56      width: 100%;
    57      height: 100%;
    58    }
    59    #trace-viewer {
    60      width: 100%;
    61      height: 100%;
    62    }
    63    #trace-viewer:focus {
    64      outline: none;
    65    }
    66  </style>
    67  <script>
    68  'use strict';
    69  (function() {
    70    var viewer;
    71    var url;
    72    var model;
    73  
    74    function load() {
    75      var req = new XMLHttpRequest();
    76      var is_binary = /[.]gz$/.test(url) || /[.]zip$/.test(url);
    77      req.overrideMimeType('text/plain; charset=x-user-defined');
    78      req.open('GET', url, true);
    79      if (is_binary)
    80        req.responseType = 'arraybuffer';
    81  
    82      req.onreadystatechange = function(event) {
    83        if (req.readyState !== 4)
    84          return;
    85  
    86        window.setTimeout(function() {
    87          if (req.status === 200)
    88            onResult(is_binary ? req.response : req.responseText);
    89          else
    90            onResultFail(req.status);
    91        }, 0);
    92      };
    93      req.send(null);
    94    }
    95  
    96    function onResultFail(err) {
    97      var overlay = new tr.ui.b.Overlay();
    98      overlay.textContent = err + ': ' + url + ' could not be loaded';
    99      overlay.title = 'Failed to fetch data';
   100      overlay.visible = true;
   101    }
   102  
   103    function onResult(result) {
   104      model = new tr.Model();
   105      var opts = new tr.importer.ImportOptions();
   106      opts.shiftWorldToZero = false;
   107      var i = new tr.importer.Import(model, opts);
   108      var p = i.importTracesWithProgressDialog([result]);
   109      p.then(onModelLoaded, onImportFail);
   110    }
   111  
   112    function onModelLoaded() {
   113      viewer.model = model;
   114      viewer.viewTitle = "trace";
   115    }
   116  
   117    function onImportFail(err) {
   118      var overlay = new tr.ui.b.Overlay();
   119      overlay.textContent = tr.b.normalizeException(err).message;
   120      overlay.title = 'Import error';
   121      overlay.visible = true;
   122    }
   123  
   124    document.addEventListener('DOMContentLoaded', function() {
   125      var container = document.createElement('track-view-container');
   126      container.id = 'track_view_container';
   127  
   128      viewer = document.createElement('tr-ui-timeline-view');
   129      viewer.track_view_container = container;
   130      viewer.appendChild(container);
   131  
   132      viewer.id = 'trace-viewer';
   133      viewer.globalMode = true;
   134      document.body.appendChild(viewer);
   135  
   136      url = '/jsontrace?{{PARAMS}}';
   137      load();
   138    });
   139  }());
   140  </script>
   141  </head>
   142  <body>
   143  </body>
   144  </html>
   145  `
   146  
   147  // httpTraceViewerHTML serves static part of trace-viewer.
   148  // This URL is queried from templTrace HTML.
   149  func httpTraceViewerHTML(w http.ResponseWriter, r *http.Request) {
   150  	http.ServeFile(w, r, filepath.Join(runtime.GOROOT(), "misc", "trace", "trace_viewer_full.html"))
   151  }
   152  
   153  // httpJsonTrace serves json trace, requested from within templTrace HTML.
   154  func httpJsonTrace(w http.ResponseWriter, r *http.Request) {
   155  	// This is an AJAX handler, so instead of http.Error we use log.Printf to log errors.
   156  	res, err := parseTrace()
   157  	if err != nil {
   158  		log.Printf("failed to parse trace: %v", err)
   159  		return
   160  	}
   161  
   162  	params := &traceParams{
   163  		parsed:  res,
   164  		endTime: int64(1<<63 - 1),
   165  	}
   166  
   167  	if goids := r.FormValue("goid"); goids != "" {
   168  		// If goid argument is present, we are rendering a trace for this particular goroutine.
   169  		goid, err := strconv.ParseUint(goids, 10, 64)
   170  		if err != nil {
   171  			log.Printf("failed to parse goid parameter '%v': %v", goids, err)
   172  			return
   173  		}
   174  		analyzeGoroutines(res.Events)
   175  		g := gs[goid]
   176  		params.gtrace = true
   177  		params.startTime = g.StartTime
   178  		params.endTime = g.EndTime
   179  		params.maing = goid
   180  		params.gs = trace.RelatedGoroutines(res.Events, goid)
   181  	}
   182  
   183  	data, err := generateTrace(params)
   184  	if err != nil {
   185  		log.Printf("failed to generate trace: %v", err)
   186  		return
   187  	}
   188  
   189  	if startStr, endStr := r.FormValue("start"), r.FormValue("end"); startStr != "" && endStr != "" {
   190  		// If start/end arguments are present, we are rendering a range of the trace.
   191  		start, err := strconv.ParseUint(startStr, 10, 64)
   192  		if err != nil {
   193  			log.Printf("failed to parse start parameter '%v': %v", startStr, err)
   194  			return
   195  		}
   196  		end, err := strconv.ParseUint(endStr, 10, 64)
   197  		if err != nil {
   198  			log.Printf("failed to parse end parameter '%v': %v", endStr, err)
   199  			return
   200  		}
   201  		if start >= uint64(len(data.Events)) || end <= start || end > uint64(len(data.Events)) {
   202  			log.Printf("bogus start/end parameters: %v/%v, trace size %v", start, end, len(data.Events))
   203  			return
   204  		}
   205  		data.Events = append(data.Events[start:end], data.Events[data.footer:]...)
   206  	}
   207  	err = json.NewEncoder(w).Encode(data)
   208  	if err != nil {
   209  		log.Printf("failed to serialize trace: %v", err)
   210  		return
   211  	}
   212  }
   213  
   214  type Range struct {
   215  	Name  string
   216  	Start int
   217  	End   int
   218  }
   219  
   220  // splitTrace splits the trace into a number of ranges,
   221  // each resulting in approx 100MB of json output (trace viewer can hardly handle more).
   222  func splitTrace(data ViewerData) []Range {
   223  	const rangeSize = 100 << 20
   224  	var ranges []Range
   225  	cw := new(countingWriter)
   226  	enc := json.NewEncoder(cw)
   227  	// First calculate size of the mandatory part of the trace.
   228  	// This includes stack traces and thread names.
   229  	data1 := data
   230  	data1.Events = data.Events[data.footer:]
   231  	enc.Encode(data1)
   232  	auxSize := cw.size
   233  	cw.size = 0
   234  	// Then calculate size of each individual event and group them into ranges.
   235  	for i, start := 0, 0; i < data.footer; i++ {
   236  		enc.Encode(data.Events[i])
   237  		if cw.size+auxSize > rangeSize || i == data.footer-1 {
   238  			ranges = append(ranges, Range{
   239  				Name:  fmt.Sprintf("%v-%v", time.Duration(data.Events[start].Time*1000), time.Duration(data.Events[i].Time*1000)),
   240  				Start: start,
   241  				End:   i + 1,
   242  			})
   243  			start = i + 1
   244  			cw.size = 0
   245  		}
   246  	}
   247  	if len(ranges) == 1 {
   248  		ranges = nil
   249  	}
   250  	return ranges
   251  }
   252  
   253  type countingWriter struct {
   254  	size int
   255  }
   256  
   257  func (cw *countingWriter) Write(data []byte) (int, error) {
   258  	cw.size += len(data)
   259  	return len(data), nil
   260  }
   261  
   262  type traceParams struct {
   263  	parsed    trace.ParseResult
   264  	gtrace    bool
   265  	startTime int64
   266  	endTime   int64
   267  	maing     uint64
   268  	gs        map[uint64]bool
   269  }
   270  
   271  type traceContext struct {
   272  	*traceParams
   273  	data      ViewerData
   274  	frameTree frameNode
   275  	frameSeq  int
   276  	arrowSeq  uint64
   277  	gcount    uint64
   278  
   279  	heapStats, prevHeapStats     heapStats
   280  	threadStats, prevThreadStats threadStats
   281  	gstates, prevGstates         [gStateCount]int64
   282  }
   283  
   284  type heapStats struct {
   285  	heapAlloc uint64
   286  	nextGC    uint64
   287  }
   288  
   289  type threadStats struct {
   290  	insyscallRuntime uint64 // system goroutine in syscall
   291  	insyscall        uint64 // user goroutine in syscall
   292  	prunning         uint64 // thread running P
   293  }
   294  
   295  type frameNode struct {
   296  	id       int
   297  	children map[uint64]frameNode
   298  }
   299  
   300  type gState int
   301  
   302  const (
   303  	gDead gState = iota
   304  	gRunnable
   305  	gRunning
   306  	gWaiting
   307  	gWaitingGC
   308  
   309  	gStateCount
   310  )
   311  
   312  type gInfo struct {
   313  	state      gState // current state
   314  	name       string // name chosen for this goroutine at first EvGoStart
   315  	isSystemG  bool
   316  	start      *trace.Event // most recent EvGoStart
   317  	markAssist *trace.Event // if non-nil, the mark assist currently running.
   318  }
   319  
   320  type ViewerData struct {
   321  	Events   []*ViewerEvent         `json:"traceEvents"`
   322  	Frames   map[string]ViewerFrame `json:"stackFrames"`
   323  	TimeUnit string                 `json:"displayTimeUnit"`
   324  
   325  	// This is where mandatory part of the trace starts (e.g. thread names)
   326  	footer int
   327  }
   328  
   329  type ViewerEvent struct {
   330  	Name     string      `json:"name,omitempty"`
   331  	Phase    string      `json:"ph"`
   332  	Scope    string      `json:"s,omitempty"`
   333  	Time     float64     `json:"ts"`
   334  	Dur      float64     `json:"dur,omitempty"`
   335  	Pid      uint64      `json:"pid"`
   336  	Tid      uint64      `json:"tid"`
   337  	ID       uint64      `json:"id,omitempty"`
   338  	Stack    int         `json:"sf,omitempty"`
   339  	EndStack int         `json:"esf,omitempty"`
   340  	Arg      interface{} `json:"args,omitempty"`
   341  }
   342  
   343  type ViewerFrame struct {
   344  	Name   string `json:"name"`
   345  	Parent int    `json:"parent,omitempty"`
   346  }
   347  
   348  type NameArg struct {
   349  	Name string `json:"name"`
   350  }
   351  
   352  type SortIndexArg struct {
   353  	Index int `json:"sort_index"`
   354  }
   355  
   356  // generateTrace generates json trace for trace-viewer:
   357  // https://github.com/google/trace-viewer
   358  // Trace format is described at:
   359  // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/view
   360  // If gtrace=true, generate trace for goroutine goid, otherwise whole trace.
   361  // startTime, endTime determine part of the trace that we are interested in.
   362  // gset restricts goroutines that are included in the resulting trace.
   363  func generateTrace(params *traceParams) (ViewerData, error) {
   364  	ctx := &traceContext{traceParams: params}
   365  	ctx.frameTree.children = make(map[uint64]frameNode)
   366  	ctx.data.Frames = make(map[string]ViewerFrame)
   367  	ctx.data.TimeUnit = "ns"
   368  	maxProc := 0
   369  	ginfos := make(map[uint64]*gInfo)
   370  	stacks := params.parsed.Stacks
   371  
   372  	getGInfo := func(g uint64) *gInfo {
   373  		info, ok := ginfos[g]
   374  		if !ok {
   375  			info = &gInfo{}
   376  			ginfos[g] = info
   377  		}
   378  		return info
   379  	}
   380  
   381  	// Since we make many calls to setGState, we record a sticky
   382  	// error in setGStateErr and check it after every event.
   383  	var setGStateErr error
   384  	setGState := func(ev *trace.Event, g uint64, oldState, newState gState) {
   385  		info := getGInfo(g)
   386  		if oldState == gWaiting && info.state == gWaitingGC {
   387  			// For checking, gWaiting counts as any gWaiting*.
   388  			oldState = info.state
   389  		}
   390  		if info.state != oldState && setGStateErr == nil {
   391  			setGStateErr = fmt.Errorf("expected G %d to be in state %d, but got state %d", g, oldState, newState)
   392  		}
   393  		ctx.gstates[info.state]--
   394  		ctx.gstates[newState]++
   395  		info.state = newState
   396  	}
   397  
   398  	for _, ev := range ctx.parsed.Events {
   399  		// Handle state transitions before we filter out events.
   400  		switch ev.Type {
   401  		case trace.EvGoStart, trace.EvGoStartLabel:
   402  			setGState(ev, ev.G, gRunnable, gRunning)
   403  			info := getGInfo(ev.G)
   404  			info.start = ev
   405  		case trace.EvProcStart:
   406  			ctx.threadStats.prunning++
   407  		case trace.EvProcStop:
   408  			ctx.threadStats.prunning--
   409  		case trace.EvGoCreate:
   410  			newG := ev.Args[0]
   411  			info := getGInfo(newG)
   412  			if info.name != "" {
   413  				return ctx.data, fmt.Errorf("duplicate go create event for go id=%d detected at offset %d", newG, ev.Off)
   414  			}
   415  
   416  			stk, ok := stacks[ev.Args[1]]
   417  			if !ok || len(stk) == 0 {
   418  				return ctx.data, fmt.Errorf("invalid go create event: missing stack information for go id=%d at offset %d", newG, ev.Off)
   419  			}
   420  
   421  			fname := stk[0].Fn
   422  			info.name = fmt.Sprintf("G%v %s", newG, fname)
   423  			info.isSystemG = strings.HasPrefix(fname, "runtime.") && fname != "runtime.main"
   424  
   425  			ctx.gcount++
   426  			setGState(ev, newG, gDead, gRunnable)
   427  		case trace.EvGoEnd:
   428  			ctx.gcount--
   429  			setGState(ev, ev.G, gRunning, gDead)
   430  		case trace.EvGoUnblock:
   431  			setGState(ev, ev.Args[0], gWaiting, gRunnable)
   432  		case trace.EvGoSysExit:
   433  			setGState(ev, ev.G, gWaiting, gRunnable)
   434  			if getGInfo(ev.G).isSystemG {
   435  				ctx.threadStats.insyscallRuntime--
   436  			} else {
   437  				ctx.threadStats.insyscall--
   438  			}
   439  		case trace.EvGoSysBlock:
   440  			setGState(ev, ev.G, gRunning, gWaiting)
   441  			if getGInfo(ev.G).isSystemG {
   442  				ctx.threadStats.insyscallRuntime++
   443  			} else {
   444  				ctx.threadStats.insyscall++
   445  			}
   446  		case trace.EvGoSched, trace.EvGoPreempt:
   447  			setGState(ev, ev.G, gRunning, gRunnable)
   448  		case trace.EvGoStop,
   449  			trace.EvGoSleep, trace.EvGoBlock, trace.EvGoBlockSend, trace.EvGoBlockRecv,
   450  			trace.EvGoBlockSelect, trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockNet:
   451  			setGState(ev, ev.G, gRunning, gWaiting)
   452  		case trace.EvGoBlockGC:
   453  			setGState(ev, ev.G, gRunning, gWaitingGC)
   454  		case trace.EvGCMarkAssistStart:
   455  			getGInfo(ev.G).markAssist = ev
   456  		case trace.EvGCMarkAssistDone:
   457  			getGInfo(ev.G).markAssist = nil
   458  		case trace.EvGoWaiting:
   459  			setGState(ev, ev.G, gRunnable, gWaiting)
   460  		case trace.EvGoInSyscall:
   461  			// Cancel out the effect of EvGoCreate at the beginning.
   462  			setGState(ev, ev.G, gRunnable, gWaiting)
   463  			if getGInfo(ev.G).isSystemG {
   464  				ctx.threadStats.insyscallRuntime++
   465  			} else {
   466  				ctx.threadStats.insyscall++
   467  			}
   468  		case trace.EvHeapAlloc:
   469  			ctx.heapStats.heapAlloc = ev.Args[0]
   470  		case trace.EvNextGC:
   471  			ctx.heapStats.nextGC = ev.Args[0]
   472  		}
   473  		if setGStateErr != nil {
   474  			return ctx.data, setGStateErr
   475  		}
   476  		if ctx.gstates[gRunnable] < 0 || ctx.gstates[gRunning] < 0 || ctx.threadStats.insyscall < 0 || ctx.threadStats.insyscallRuntime < 0 {
   477  			return ctx.data, fmt.Errorf("invalid state after processing %v: runnable=%d running=%d insyscall=%d insyscallRuntime=%d", ev, ctx.gstates[gRunnable], ctx.gstates[gRunning], ctx.threadStats.insyscall, ctx.threadStats.insyscallRuntime)
   478  		}
   479  
   480  		// Ignore events that are from uninteresting goroutines
   481  		// or outside of the interesting timeframe.
   482  		if ctx.gs != nil && ev.P < trace.FakeP && !ctx.gs[ev.G] {
   483  			continue
   484  		}
   485  		if ev.Ts < ctx.startTime || ev.Ts > ctx.endTime {
   486  			continue
   487  		}
   488  
   489  		if ev.P < trace.FakeP && ev.P > maxProc {
   490  			maxProc = ev.P
   491  		}
   492  
   493  		// Emit trace objects.
   494  		switch ev.Type {
   495  		case trace.EvProcStart:
   496  			if ctx.gtrace {
   497  				continue
   498  			}
   499  			ctx.emitInstant(ev, "proc start")
   500  		case trace.EvProcStop:
   501  			if ctx.gtrace {
   502  				continue
   503  			}
   504  			ctx.emitInstant(ev, "proc stop")
   505  		case trace.EvGCStart:
   506  			ctx.emitSlice(ev, "GC")
   507  		case trace.EvGCDone:
   508  		case trace.EvGCSTWStart:
   509  			if ctx.gtrace {
   510  				continue
   511  			}
   512  			ctx.emitSlice(ev, fmt.Sprintf("STW (%s)", ev.SArgs[0]))
   513  		case trace.EvGCSTWDone:
   514  		case trace.EvGCMarkAssistStart:
   515  			// Mark assists can continue past preemptions, so truncate to the
   516  			// whichever comes first. We'll synthesize another slice if
   517  			// necessary in EvGoStart.
   518  			markFinish := ev.Link
   519  			goFinish := getGInfo(ev.G).start.Link
   520  			fakeMarkStart := *ev
   521  			text := "MARK ASSIST"
   522  			if markFinish == nil || markFinish.Ts > goFinish.Ts {
   523  				fakeMarkStart.Link = goFinish
   524  				text = "MARK ASSIST (unfinished)"
   525  			}
   526  			ctx.emitSlice(&fakeMarkStart, text)
   527  		case trace.EvGCSweepStart:
   528  			slice := ctx.emitSlice(ev, "SWEEP")
   529  			if done := ev.Link; done != nil && done.Args[0] != 0 {
   530  				slice.Arg = struct {
   531  					Swept     uint64 `json:"Swept bytes"`
   532  					Reclaimed uint64 `json:"Reclaimed bytes"`
   533  				}{done.Args[0], done.Args[1]}
   534  			}
   535  		case trace.EvGoStart, trace.EvGoStartLabel:
   536  			info := getGInfo(ev.G)
   537  			if ev.Type == trace.EvGoStartLabel {
   538  				ctx.emitSlice(ev, ev.SArgs[0])
   539  			} else {
   540  				ctx.emitSlice(ev, info.name)
   541  			}
   542  			if info.markAssist != nil {
   543  				// If we're in a mark assist, synthesize a new slice, ending
   544  				// either when the mark assist ends or when we're descheduled.
   545  				markFinish := info.markAssist.Link
   546  				goFinish := ev.Link
   547  				fakeMarkStart := *ev
   548  				text := "MARK ASSIST (resumed, unfinished)"
   549  				if markFinish != nil && markFinish.Ts < goFinish.Ts {
   550  					fakeMarkStart.Link = markFinish
   551  					text = "MARK ASSIST (resumed)"
   552  				}
   553  				ctx.emitSlice(&fakeMarkStart, text)
   554  			}
   555  		case trace.EvGoCreate:
   556  			ctx.emitArrow(ev, "go")
   557  		case trace.EvGoUnblock:
   558  			ctx.emitArrow(ev, "unblock")
   559  		case trace.EvGoSysCall:
   560  			ctx.emitInstant(ev, "syscall")
   561  		case trace.EvGoSysExit:
   562  			ctx.emitArrow(ev, "sysexit")
   563  		}
   564  		// Emit any counter updates.
   565  		ctx.emitThreadCounters(ev)
   566  		ctx.emitHeapCounters(ev)
   567  		ctx.emitGoroutineCounters(ev)
   568  	}
   569  
   570  	ctx.data.footer = len(ctx.data.Events)
   571  	ctx.emit(&ViewerEvent{Name: "process_name", Phase: "M", Pid: 0, Arg: &NameArg{"PROCS"}})
   572  	ctx.emit(&ViewerEvent{Name: "process_sort_index", Phase: "M", Pid: 0, Arg: &SortIndexArg{1}})
   573  
   574  	ctx.emit(&ViewerEvent{Name: "process_name", Phase: "M", Pid: 1, Arg: &NameArg{"STATS"}})
   575  	ctx.emit(&ViewerEvent{Name: "process_sort_index", Phase: "M", Pid: 1, Arg: &SortIndexArg{0}})
   576  
   577  	ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.GCP, Arg: &NameArg{"GC"}})
   578  	ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.GCP, Arg: &SortIndexArg{-6}})
   579  
   580  	ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.NetpollP, Arg: &NameArg{"Network"}})
   581  	ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.NetpollP, Arg: &SortIndexArg{-5}})
   582  
   583  	ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.TimerP, Arg: &NameArg{"Timers"}})
   584  	ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.TimerP, Arg: &SortIndexArg{-4}})
   585  
   586  	ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.SyscallP, Arg: &NameArg{"Syscalls"}})
   587  	ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.SyscallP, Arg: &SortIndexArg{-3}})
   588  
   589  	if !ctx.gtrace {
   590  		for i := 0; i <= maxProc; i++ {
   591  			ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: uint64(i), Arg: &NameArg{fmt.Sprintf("Proc %v", i)}})
   592  			ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: uint64(i), Arg: &SortIndexArg{i}})
   593  		}
   594  	}
   595  
   596  	if ctx.gtrace && ctx.gs != nil {
   597  		for k, v := range ginfos {
   598  			if !ctx.gs[k] {
   599  				continue
   600  			}
   601  			ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: k, Arg: &NameArg{v.name}})
   602  		}
   603  		ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: ctx.maing, Arg: &SortIndexArg{-2}})
   604  		ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: 0, Arg: &SortIndexArg{-1}})
   605  	}
   606  
   607  	return ctx.data, nil
   608  }
   609  
   610  func (ctx *traceContext) emit(e *ViewerEvent) {
   611  	ctx.data.Events = append(ctx.data.Events, e)
   612  }
   613  
   614  func (ctx *traceContext) time(ev *trace.Event) float64 {
   615  	// Trace viewer wants timestamps in microseconds.
   616  	return float64(ev.Ts-ctx.startTime) / 1000
   617  }
   618  
   619  func (ctx *traceContext) proc(ev *trace.Event) uint64 {
   620  	if ctx.gtrace && ev.P < trace.FakeP {
   621  		return ev.G
   622  	} else {
   623  		return uint64(ev.P)
   624  	}
   625  }
   626  
   627  func (ctx *traceContext) emitSlice(ev *trace.Event, name string) *ViewerEvent {
   628  	sl := &ViewerEvent{
   629  		Name:     name,
   630  		Phase:    "X",
   631  		Time:     ctx.time(ev),
   632  		Dur:      ctx.time(ev.Link) - ctx.time(ev),
   633  		Tid:      ctx.proc(ev),
   634  		Stack:    ctx.stack(ev.Stk),
   635  		EndStack: ctx.stack(ev.Link.Stk),
   636  	}
   637  	ctx.emit(sl)
   638  	return sl
   639  }
   640  
   641  type heapCountersArg struct {
   642  	Allocated uint64
   643  	NextGC    uint64
   644  }
   645  
   646  func (ctx *traceContext) emitHeapCounters(ev *trace.Event) {
   647  	if ctx.gtrace {
   648  		return
   649  	}
   650  	if ctx.prevHeapStats == ctx.heapStats {
   651  		return
   652  	}
   653  	diff := uint64(0)
   654  	if ctx.heapStats.nextGC > ctx.heapStats.heapAlloc {
   655  		diff = ctx.heapStats.nextGC - ctx.heapStats.heapAlloc
   656  	}
   657  	ctx.emit(&ViewerEvent{Name: "Heap", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &heapCountersArg{ctx.heapStats.heapAlloc, diff}})
   658  	ctx.prevHeapStats = ctx.heapStats
   659  }
   660  
   661  type goroutineCountersArg struct {
   662  	Running   uint64
   663  	Runnable  uint64
   664  	GCWaiting uint64
   665  }
   666  
   667  func (ctx *traceContext) emitGoroutineCounters(ev *trace.Event) {
   668  	if ctx.gtrace {
   669  		return
   670  	}
   671  	if ctx.prevGstates == ctx.gstates {
   672  		return
   673  	}
   674  	ctx.emit(&ViewerEvent{Name: "Goroutines", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &goroutineCountersArg{uint64(ctx.gstates[gRunning]), uint64(ctx.gstates[gRunnable]), uint64(ctx.gstates[gWaitingGC])}})
   675  	ctx.prevGstates = ctx.gstates
   676  }
   677  
   678  type threadCountersArg struct {
   679  	Running   uint64
   680  	InSyscall uint64
   681  }
   682  
   683  func (ctx *traceContext) emitThreadCounters(ev *trace.Event) {
   684  	if ctx.gtrace {
   685  		return
   686  	}
   687  	if ctx.prevThreadStats == ctx.threadStats {
   688  		return
   689  	}
   690  	ctx.emit(&ViewerEvent{Name: "Threads", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &threadCountersArg{
   691  		Running:   ctx.threadStats.prunning,
   692  		InSyscall: ctx.threadStats.insyscall}})
   693  	ctx.prevThreadStats = ctx.threadStats
   694  }
   695  
   696  func (ctx *traceContext) emitInstant(ev *trace.Event, name string) {
   697  	var arg interface{}
   698  	if ev.Type == trace.EvProcStart {
   699  		type Arg struct {
   700  			ThreadID uint64
   701  		}
   702  		arg = &Arg{ev.Args[0]}
   703  	}
   704  	ctx.emit(&ViewerEvent{Name: name, Phase: "I", Scope: "t", Time: ctx.time(ev), Tid: ctx.proc(ev), Stack: ctx.stack(ev.Stk), Arg: arg})
   705  }
   706  
   707  func (ctx *traceContext) emitArrow(ev *trace.Event, name string) {
   708  	if ev.Link == nil {
   709  		// The other end of the arrow is not captured in the trace.
   710  		// For example, a goroutine was unblocked but was not scheduled before trace stop.
   711  		return
   712  	}
   713  	if ctx.gtrace && (!ctx.gs[ev.Link.G] || ev.Link.Ts < ctx.startTime || ev.Link.Ts > ctx.endTime) {
   714  		return
   715  	}
   716  
   717  	if ev.P == trace.NetpollP || ev.P == trace.TimerP || ev.P == trace.SyscallP {
   718  		// Trace-viewer discards arrows if they don't start/end inside of a slice or instant.
   719  		// So emit a fake instant at the start of the arrow.
   720  		ctx.emitInstant(&trace.Event{P: ev.P, Ts: ev.Ts}, "unblock")
   721  	}
   722  
   723  	ctx.arrowSeq++
   724  	ctx.emit(&ViewerEvent{Name: name, Phase: "s", Tid: ctx.proc(ev), ID: ctx.arrowSeq, Time: ctx.time(ev), Stack: ctx.stack(ev.Stk)})
   725  	ctx.emit(&ViewerEvent{Name: name, Phase: "t", Tid: ctx.proc(ev.Link), ID: ctx.arrowSeq, Time: ctx.time(ev.Link)})
   726  }
   727  
   728  func (ctx *traceContext) stack(stk []*trace.Frame) int {
   729  	return ctx.buildBranch(ctx.frameTree, stk)
   730  }
   731  
   732  // buildBranch builds one branch in the prefix tree rooted at ctx.frameTree.
   733  func (ctx *traceContext) buildBranch(parent frameNode, stk []*trace.Frame) int {
   734  	if len(stk) == 0 {
   735  		return parent.id
   736  	}
   737  	last := len(stk) - 1
   738  	frame := stk[last]
   739  	stk = stk[:last]
   740  
   741  	node, ok := parent.children[frame.PC]
   742  	if !ok {
   743  		ctx.frameSeq++
   744  		node.id = ctx.frameSeq
   745  		node.children = make(map[uint64]frameNode)
   746  		parent.children[frame.PC] = node
   747  		ctx.data.Frames[strconv.Itoa(node.id)] = ViewerFrame{fmt.Sprintf("%v:%v", frame.Fn, frame.Line), parent.id}
   748  	}
   749  	return ctx.buildBranch(node, stk)
   750  }