Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/trace/trace.go

Documentation: cmd/trace

     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  	"io"
    12  	"log"
    13  	"math"
    14  	"net/http"
    15  	"path/filepath"
    16  	"runtime"
    17  	"runtime/debug"
    18  	"sort"
    19  	"strconv"
    20  	"strings"
    21  	"time"
    22  )
    23  
    24  func init() {
    25  	http.HandleFunc("/trace", httpTrace)
    26  	http.HandleFunc("/jsontrace", httpJsonTrace)
    27  	http.HandleFunc("/trace_viewer_html", httpTraceViewerHTML)
    28  	http.HandleFunc("/webcomponents.min.js", webcomponentsJS)
    29  }
    30  
    31  // httpTrace serves either whole trace (goid==0) or trace for goid goroutine.
    32  func httpTrace(w http.ResponseWriter, r *http.Request) {
    33  	_, err := parseTrace()
    34  	if err != nil {
    35  		http.Error(w, err.Error(), http.StatusInternalServerError)
    36  		return
    37  	}
    38  	if err := r.ParseForm(); err != nil {
    39  		http.Error(w, err.Error(), http.StatusInternalServerError)
    40  		return
    41  	}
    42  	html := strings.ReplaceAll(templTrace, "{{PARAMS}}", r.Form.Encode())
    43  	w.Write([]byte(html))
    44  
    45  }
    46  
    47  // https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962/tracing/docs/embedding-trace-viewer.md
    48  // This is almost verbatim copy of https://chromium-review.googlesource.com/c/catapult/+/2062938/2/tracing/bin/index.html
    49  var templTrace = `
    50  <html>
    51  <head>
    52  <script src="/webcomponents.min.js"></script>
    53  <script>
    54  'use strict';
    55  
    56  function onTraceViewerImportFail() {
    57    document.addEventListener('DOMContentLoaded', function() {
    58      document.body.textContent =
    59      '/trace_viewer_full.html is missing. File a bug in https://golang.org/issue';
    60    });
    61  }
    62  </script>
    63  
    64  <link rel="import" href="/trace_viewer_html"
    65        onerror="onTraceViewerImportFail(event)">
    66  
    67  <style type="text/css">
    68    html, body {
    69      box-sizing: border-box;
    70      overflow: hidden;
    71      margin: 0px;
    72      padding: 0;
    73      width: 100%;
    74      height: 100%;
    75    }
    76    #trace-viewer {
    77      width: 100%;
    78      height: 100%;
    79    }
    80    #trace-viewer:focus {
    81      outline: none;
    82    }
    83  </style>
    84  <script>
    85  'use strict';
    86  (function() {
    87    var viewer;
    88    var url;
    89    var model;
    90  
    91    function load() {
    92      var req = new XMLHttpRequest();
    93      var isBinary = /[.]gz$/.test(url) || /[.]zip$/.test(url);
    94      req.overrideMimeType('text/plain; charset=x-user-defined');
    95      req.open('GET', url, true);
    96      if (isBinary)
    97        req.responseType = 'arraybuffer';
    98  
    99      req.onreadystatechange = function(event) {
   100        if (req.readyState !== 4)
   101          return;
   102  
   103        window.setTimeout(function() {
   104          if (req.status === 200)
   105            onResult(isBinary ? req.response : req.responseText);
   106          else
   107            onResultFail(req.status);
   108        }, 0);
   109      };
   110      req.send(null);
   111    }
   112  
   113    function onResultFail(err) {
   114      var overlay = new tr.ui.b.Overlay();
   115      overlay.textContent = err + ': ' + url + ' could not be loaded';
   116      overlay.title = 'Failed to fetch data';
   117      overlay.visible = true;
   118    }
   119  
   120    function onResult(result) {
   121      model = new tr.Model();
   122      var opts = new tr.importer.ImportOptions();
   123      opts.shiftWorldToZero = false;
   124      var i = new tr.importer.Import(model, opts);
   125      var p = i.importTracesWithProgressDialog([result]);
   126      p.then(onModelLoaded, onImportFail);
   127    }
   128  
   129    function onModelLoaded() {
   130      viewer.model = model;
   131      viewer.viewTitle = "trace";
   132  
   133      if (!model || model.bounds.isEmpty)
   134        return;
   135      var sel = window.location.hash.substr(1);
   136      if (sel === '')
   137        return;
   138      var parts = sel.split(':');
   139      var range = new (tr.b.Range || tr.b.math.Range)();
   140      range.addValue(parseFloat(parts[0]));
   141      range.addValue(parseFloat(parts[1]));
   142      viewer.trackView.viewport.interestRange.set(range);
   143    }
   144  
   145    function onImportFail(err) {
   146      var overlay = new tr.ui.b.Overlay();
   147      overlay.textContent = tr.b.normalizeException(err).message;
   148      overlay.title = 'Import error';
   149      overlay.visible = true;
   150    }
   151  
   152    document.addEventListener('WebComponentsReady', function() {
   153      var container = document.createElement('track-view-container');
   154      container.id = 'track_view_container';
   155  
   156      viewer = document.createElement('tr-ui-timeline-view');
   157      viewer.track_view_container = container;
   158      Polymer.dom(viewer).appendChild(container);
   159  
   160      viewer.id = 'trace-viewer';
   161      viewer.globalMode = true;
   162      Polymer.dom(document.body).appendChild(viewer);
   163  
   164      url = '/jsontrace?{{PARAMS}}';
   165      load();
   166    });
   167  }());
   168  </script>
   169  </head>
   170  <body>
   171  </body>
   172  </html>
   173  `
   174  
   175  // httpTraceViewerHTML serves static part of trace-viewer.
   176  // This URL is queried from templTrace HTML.
   177  func httpTraceViewerHTML(w http.ResponseWriter, r *http.Request) {
   178  	http.ServeFile(w, r, filepath.Join(runtime.GOROOT(), "misc", "trace", "trace_viewer_full.html"))
   179  }
   180  
   181  func webcomponentsJS(w http.ResponseWriter, r *http.Request) {
   182  	http.ServeFile(w, r, filepath.Join(runtime.GOROOT(), "misc", "trace", "webcomponents.min.js"))
   183  }
   184  
   185  // httpJsonTrace serves json trace, requested from within templTrace HTML.
   186  func httpJsonTrace(w http.ResponseWriter, r *http.Request) {
   187  	defer debug.FreeOSMemory()
   188  	defer reportMemoryUsage("after httpJsonTrace")
   189  	// This is an AJAX handler, so instead of http.Error we use log.Printf to log errors.
   190  	res, err := parseTrace()
   191  	if err != nil {
   192  		log.Printf("failed to parse trace: %v", err)
   193  		return
   194  	}
   195  
   196  	params := &traceParams{
   197  		parsed:  res,
   198  		endTime: math.MaxInt64,
   199  	}
   200  
   201  	if goids := r.FormValue("goid"); goids != "" {
   202  		// If goid argument is present, we are rendering a trace for this particular goroutine.
   203  		goid, err := strconv.ParseUint(goids, 10, 64)
   204  		if err != nil {
   205  			log.Printf("failed to parse goid parameter %q: %v", goids, err)
   206  			return
   207  		}
   208  		analyzeGoroutines(res.Events)
   209  		g, ok := gs[goid]
   210  		if !ok {
   211  			log.Printf("failed to find goroutine %d", goid)
   212  			return
   213  		}
   214  		params.mode = modeGoroutineOriented
   215  		params.startTime = g.StartTime
   216  		if g.EndTime != 0 {
   217  			params.endTime = g.EndTime
   218  		} else { // The goroutine didn't end.
   219  			params.endTime = lastTimestamp()
   220  		}
   221  		params.maing = goid
   222  		params.gs = trace.RelatedGoroutines(res.Events, goid)
   223  	} else if taskids := r.FormValue("taskid"); taskids != "" {
   224  		taskid, err := strconv.ParseUint(taskids, 10, 64)
   225  		if err != nil {
   226  			log.Printf("failed to parse taskid parameter %q: %v", taskids, err)
   227  			return
   228  		}
   229  		annotRes, _ := analyzeAnnotations()
   230  		task, ok := annotRes.tasks[taskid]
   231  		if !ok || len(task.events) == 0 {
   232  			log.Printf("failed to find task with id %d", taskid)
   233  			return
   234  		}
   235  		goid := task.events[0].G
   236  		params.mode = modeGoroutineOriented | modeTaskOriented
   237  		params.startTime = task.firstTimestamp() - 1
   238  		params.endTime = task.lastTimestamp() + 1
   239  		params.maing = goid
   240  		params.tasks = task.descendants()
   241  		gs := map[uint64]bool{}
   242  		for _, t := range params.tasks {
   243  			// find only directly involved goroutines
   244  			for k, v := range t.RelatedGoroutines(res.Events, 0) {
   245  				gs[k] = v
   246  			}
   247  		}
   248  		params.gs = gs
   249  	} else if taskids := r.FormValue("focustask"); taskids != "" {
   250  		taskid, err := strconv.ParseUint(taskids, 10, 64)
   251  		if err != nil {
   252  			log.Printf("failed to parse focustask parameter %q: %v", taskids, err)
   253  			return
   254  		}
   255  		annotRes, _ := analyzeAnnotations()
   256  		task, ok := annotRes.tasks[taskid]
   257  		if !ok || len(task.events) == 0 {
   258  			log.Printf("failed to find task with id %d", taskid)
   259  			return
   260  		}
   261  		params.mode = modeTaskOriented
   262  		params.startTime = task.firstTimestamp() - 1
   263  		params.endTime = task.lastTimestamp() + 1
   264  		params.tasks = task.descendants()
   265  	}
   266  
   267  	start := int64(0)
   268  	end := int64(math.MaxInt64)
   269  	if startStr, endStr := r.FormValue("start"), r.FormValue("end"); startStr != "" && endStr != "" {
   270  		// If start/end arguments are present, we are rendering a range of the trace.
   271  		start, err = strconv.ParseInt(startStr, 10, 64)
   272  		if err != nil {
   273  			log.Printf("failed to parse start parameter %q: %v", startStr, err)
   274  			return
   275  		}
   276  		end, err = strconv.ParseInt(endStr, 10, 64)
   277  		if err != nil {
   278  			log.Printf("failed to parse end parameter %q: %v", endStr, err)
   279  			return
   280  		}
   281  	}
   282  
   283  	c := viewerDataTraceConsumer(w, start, end)
   284  	if err := generateTrace(params, c); err != nil {
   285  		log.Printf("failed to generate trace: %v", err)
   286  		return
   287  	}
   288  }
   289  
   290  type Range struct {
   291  	Name      string
   292  	Start     int
   293  	End       int
   294  	StartTime int64
   295  	EndTime   int64
   296  }
   297  
   298  func (r Range) URL() string {
   299  	return fmt.Sprintf("/trace?start=%d&end=%d", r.Start, r.End)
   300  }
   301  
   302  // splitTrace splits the trace into a number of ranges,
   303  // each resulting in approx 100MB of json output
   304  // (trace viewer can hardly handle more).
   305  func splitTrace(res trace.ParseResult) []Range {
   306  	params := &traceParams{
   307  		parsed:  res,
   308  		endTime: math.MaxInt64,
   309  	}
   310  	s, c := splittingTraceConsumer(100 << 20) // 100M
   311  	if err := generateTrace(params, c); err != nil {
   312  		dief("%v\n", err)
   313  	}
   314  	return s.Ranges
   315  }
   316  
   317  type splitter struct {
   318  	Ranges []Range
   319  }
   320  
   321  func splittingTraceConsumer(max int) (*splitter, traceConsumer) {
   322  	type eventSz struct {
   323  		Time float64
   324  		Sz   int
   325  	}
   326  
   327  	var (
   328  		data = ViewerData{Frames: make(map[string]ViewerFrame)}
   329  
   330  		sizes []eventSz
   331  		cw    countingWriter
   332  	)
   333  
   334  	s := new(splitter)
   335  
   336  	return s, traceConsumer{
   337  		consumeTimeUnit: func(unit string) {
   338  			data.TimeUnit = unit
   339  		},
   340  		consumeViewerEvent: func(v *ViewerEvent, required bool) {
   341  			if required {
   342  				// Store required events inside data
   343  				// so flush can include them in the required
   344  				// part of the trace.
   345  				data.Events = append(data.Events, v)
   346  				return
   347  			}
   348  			enc := json.NewEncoder(&cw)
   349  			enc.Encode(v)
   350  			sizes = append(sizes, eventSz{v.Time, cw.size + 1}) // +1 for ",".
   351  			cw.size = 0
   352  		},
   353  		consumeViewerFrame: func(k string, v ViewerFrame) {
   354  			data.Frames[k] = v
   355  		},
   356  		flush: func() {
   357  			// Calculate size of the mandatory part of the trace.
   358  			// This includes stack traces and thread names.
   359  			cw.size = 0
   360  			enc := json.NewEncoder(&cw)
   361  			enc.Encode(data)
   362  			minSize := cw.size
   363  
   364  			// Then calculate size of each individual event
   365  			// and group them into ranges.
   366  			sum := minSize
   367  			start := 0
   368  			for i, ev := range sizes {
   369  				if sum+ev.Sz > max {
   370  					startTime := time.Duration(sizes[start].Time * 1000)
   371  					endTime := time.Duration(ev.Time * 1000)
   372  					ranges = append(ranges, Range{
   373  						Name:      fmt.Sprintf("%v-%v", startTime, endTime),
   374  						Start:     start,
   375  						End:       i + 1,
   376  						StartTime: int64(startTime),
   377  						EndTime:   int64(endTime),
   378  					})
   379  					start = i + 1
   380  					sum = minSize
   381  				} else {
   382  					sum += ev.Sz + 1
   383  				}
   384  			}
   385  			if len(ranges) <= 1 {
   386  				s.Ranges = nil
   387  				return
   388  			}
   389  
   390  			if end := len(sizes) - 1; start < end {
   391  				ranges = append(ranges, Range{
   392  					Name:      fmt.Sprintf("%v-%v", time.Duration(sizes[start].Time*1000), time.Duration(sizes[end].Time*1000)),
   393  					Start:     start,
   394  					End:       end,
   395  					StartTime: int64(sizes[start].Time * 1000),
   396  					EndTime:   int64(sizes[end].Time * 1000),
   397  				})
   398  			}
   399  			s.Ranges = ranges
   400  		},
   401  	}
   402  }
   403  
   404  type countingWriter struct {
   405  	size int
   406  }
   407  
   408  func (cw *countingWriter) Write(data []byte) (int, error) {
   409  	cw.size += len(data)
   410  	return len(data), nil
   411  }
   412  
   413  type traceParams struct {
   414  	parsed    trace.ParseResult
   415  	mode      traceviewMode
   416  	startTime int64
   417  	endTime   int64
   418  	maing     uint64          // for goroutine-oriented view, place this goroutine on the top row
   419  	gs        map[uint64]bool // Goroutines to be displayed for goroutine-oriented or task-oriented view
   420  	tasks     []*taskDesc     // Tasks to be displayed. tasks[0] is the top-most task
   421  }
   422  
   423  type traceviewMode uint
   424  
   425  const (
   426  	modeGoroutineOriented traceviewMode = 1 << iota
   427  	modeTaskOriented
   428  )
   429  
   430  type traceContext struct {
   431  	*traceParams
   432  	consumer  traceConsumer
   433  	frameTree frameNode
   434  	frameSeq  int
   435  	arrowSeq  uint64
   436  	gcount    uint64
   437  
   438  	heapStats, prevHeapStats     heapStats
   439  	threadStats, prevThreadStats threadStats
   440  	gstates, prevGstates         [gStateCount]int64
   441  
   442  	regionID int // last emitted region id. incremented in each emitRegion call.
   443  }
   444  
   445  type heapStats struct {
   446  	heapAlloc uint64
   447  	nextGC    uint64
   448  }
   449  
   450  type threadStats struct {
   451  	insyscallRuntime int64 // system goroutine in syscall
   452  	insyscall        int64 // user goroutine in syscall
   453  	prunning         int64 // thread running P
   454  }
   455  
   456  type frameNode struct {
   457  	id       int
   458  	children map[uint64]frameNode
   459  }
   460  
   461  type gState int
   462  
   463  const (
   464  	gDead gState = iota
   465  	gRunnable
   466  	gRunning
   467  	gWaiting
   468  	gWaitingGC
   469  
   470  	gStateCount
   471  )
   472  
   473  type gInfo struct {
   474  	state      gState // current state
   475  	name       string // name chosen for this goroutine at first EvGoStart
   476  	isSystemG  bool
   477  	start      *trace.Event // most recent EvGoStart
   478  	markAssist *trace.Event // if non-nil, the mark assist currently running.
   479  }
   480  
   481  type ViewerData struct {
   482  	Events   []*ViewerEvent         `json:"traceEvents"`
   483  	Frames   map[string]ViewerFrame `json:"stackFrames"`
   484  	TimeUnit string                 `json:"displayTimeUnit"`
   485  
   486  	// This is where mandatory part of the trace starts (e.g. thread names)
   487  	footer int
   488  }
   489  
   490  type ViewerEvent struct {
   491  	Name     string      `json:"name,omitempty"`
   492  	Phase    string      `json:"ph"`
   493  	Scope    string      `json:"s,omitempty"`
   494  	Time     float64     `json:"ts"`
   495  	Dur      float64     `json:"dur,omitempty"`
   496  	Pid      uint64      `json:"pid"`
   497  	Tid      uint64      `json:"tid"`
   498  	ID       uint64      `json:"id,omitempty"`
   499  	Stack    int         `json:"sf,omitempty"`
   500  	EndStack int         `json:"esf,omitempty"`
   501  	Arg      interface{} `json:"args,omitempty"`
   502  	Cname    string      `json:"cname,omitempty"`
   503  	Category string      `json:"cat,omitempty"`
   504  }
   505  
   506  type ViewerFrame struct {
   507  	Name   string `json:"name"`
   508  	Parent int    `json:"parent,omitempty"`
   509  }
   510  
   511  type NameArg struct {
   512  	Name string `json:"name"`
   513  }
   514  
   515  type TaskArg struct {
   516  	ID     uint64 `json:"id"`
   517  	StartG uint64 `json:"start_g,omitempty"`
   518  	EndG   uint64 `json:"end_g,omitempty"`
   519  }
   520  
   521  type RegionArg struct {
   522  	TaskID uint64 `json:"taskid,omitempty"`
   523  }
   524  
   525  type SortIndexArg struct {
   526  	Index int `json:"sort_index"`
   527  }
   528  
   529  type traceConsumer struct {
   530  	consumeTimeUnit    func(unit string)
   531  	consumeViewerEvent func(v *ViewerEvent, required bool)
   532  	consumeViewerFrame func(key string, f ViewerFrame)
   533  	flush              func()
   534  }
   535  
   536  const (
   537  	procsSection = 0 // where Goroutines or per-P timelines are presented.
   538  	statsSection = 1 // where counters are presented.
   539  	tasksSection = 2 // where Task hierarchy & timeline is presented.
   540  )
   541  
   542  // generateTrace generates json trace for trace-viewer:
   543  // https://github.com/google/trace-viewer
   544  // Trace format is described at:
   545  // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/view
   546  // If mode==goroutineMode, generate trace for goroutine goid, otherwise whole trace.
   547  // startTime, endTime determine part of the trace that we are interested in.
   548  // gset restricts goroutines that are included in the resulting trace.
   549  func generateTrace(params *traceParams, consumer traceConsumer) error {
   550  	defer consumer.flush()
   551  
   552  	ctx := &traceContext{traceParams: params}
   553  	ctx.frameTree.children = make(map[uint64]frameNode)
   554  	ctx.consumer = consumer
   555  
   556  	ctx.consumer.consumeTimeUnit("ns")
   557  	maxProc := 0
   558  	ginfos := make(map[uint64]*gInfo)
   559  	stacks := params.parsed.Stacks
   560  
   561  	getGInfo := func(g uint64) *gInfo {
   562  		info, ok := ginfos[g]
   563  		if !ok {
   564  			info = &gInfo{}
   565  			ginfos[g] = info
   566  		}
   567  		return info
   568  	}
   569  
   570  	// Since we make many calls to setGState, we record a sticky
   571  	// error in setGStateErr and check it after every event.
   572  	var setGStateErr error
   573  	setGState := func(ev *trace.Event, g uint64, oldState, newState gState) {
   574  		info := getGInfo(g)
   575  		if oldState == gWaiting && info.state == gWaitingGC {
   576  			// For checking, gWaiting counts as any gWaiting*.
   577  			oldState = info.state
   578  		}
   579  		if info.state != oldState && setGStateErr == nil {
   580  			setGStateErr = fmt.Errorf("expected G %d to be in state %d, but got state %d", g, oldState, newState)
   581  		}
   582  		ctx.gstates[info.state]--
   583  		ctx.gstates[newState]++
   584  		info.state = newState
   585  	}
   586  
   587  	for _, ev := range ctx.parsed.Events {
   588  		// Handle state transitions before we filter out events.
   589  		switch ev.Type {
   590  		case trace.EvGoStart, trace.EvGoStartLabel:
   591  			setGState(ev, ev.G, gRunnable, gRunning)
   592  			info := getGInfo(ev.G)
   593  			info.start = ev
   594  		case trace.EvProcStart:
   595  			ctx.threadStats.prunning++
   596  		case trace.EvProcStop:
   597  			ctx.threadStats.prunning--
   598  		case trace.EvGoCreate:
   599  			newG := ev.Args[0]
   600  			info := getGInfo(newG)
   601  			if info.name != "" {
   602  				return fmt.Errorf("duplicate go create event for go id=%d detected at offset %d", newG, ev.Off)
   603  			}
   604  
   605  			stk, ok := stacks[ev.Args[1]]
   606  			if !ok || len(stk) == 0 {
   607  				return fmt.Errorf("invalid go create event: missing stack information for go id=%d at offset %d", newG, ev.Off)
   608  			}
   609  
   610  			fname := stk[0].Fn
   611  			info.name = fmt.Sprintf("G%v %s", newG, fname)
   612  			info.isSystemG = isSystemGoroutine(fname)
   613  
   614  			ctx.gcount++
   615  			setGState(ev, newG, gDead, gRunnable)
   616  		case trace.EvGoEnd:
   617  			ctx.gcount--
   618  			setGState(ev, ev.G, gRunning, gDead)
   619  		case trace.EvGoUnblock:
   620  			setGState(ev, ev.Args[0], gWaiting, gRunnable)
   621  		case trace.EvGoSysExit:
   622  			setGState(ev, ev.G, gWaiting, gRunnable)
   623  			if getGInfo(ev.G).isSystemG {
   624  				ctx.threadStats.insyscallRuntime--
   625  			} else {
   626  				ctx.threadStats.insyscall--
   627  			}
   628  		case trace.EvGoSysBlock:
   629  			setGState(ev, ev.G, gRunning, gWaiting)
   630  			if getGInfo(ev.G).isSystemG {
   631  				ctx.threadStats.insyscallRuntime++
   632  			} else {
   633  				ctx.threadStats.insyscall++
   634  			}
   635  		case trace.EvGoSched, trace.EvGoPreempt:
   636  			setGState(ev, ev.G, gRunning, gRunnable)
   637  		case trace.EvGoStop,
   638  			trace.EvGoSleep, trace.EvGoBlock, trace.EvGoBlockSend, trace.EvGoBlockRecv,
   639  			trace.EvGoBlockSelect, trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockNet:
   640  			setGState(ev, ev.G, gRunning, gWaiting)
   641  		case trace.EvGoBlockGC:
   642  			setGState(ev, ev.G, gRunning, gWaitingGC)
   643  		case trace.EvGCMarkAssistStart:
   644  			getGInfo(ev.G).markAssist = ev
   645  		case trace.EvGCMarkAssistDone:
   646  			getGInfo(ev.G).markAssist = nil
   647  		case trace.EvGoWaiting:
   648  			setGState(ev, ev.G, gRunnable, gWaiting)
   649  		case trace.EvGoInSyscall:
   650  			// Cancel out the effect of EvGoCreate at the beginning.
   651  			setGState(ev, ev.G, gRunnable, gWaiting)
   652  			if getGInfo(ev.G).isSystemG {
   653  				ctx.threadStats.insyscallRuntime++
   654  			} else {
   655  				ctx.threadStats.insyscall++
   656  			}
   657  		case trace.EvHeapAlloc:
   658  			ctx.heapStats.heapAlloc = ev.Args[0]
   659  		case trace.EvNextGC:
   660  			ctx.heapStats.nextGC = ev.Args[0]
   661  		}
   662  		if setGStateErr != nil {
   663  			return setGStateErr
   664  		}
   665  		if ctx.gstates[gRunnable] < 0 || ctx.gstates[gRunning] < 0 || ctx.threadStats.insyscall < 0 || ctx.threadStats.insyscallRuntime < 0 {
   666  			return 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)
   667  		}
   668  
   669  		// Ignore events that are from uninteresting goroutines
   670  		// or outside of the interesting timeframe.
   671  		if ctx.gs != nil && ev.P < trace.FakeP && !ctx.gs[ev.G] {
   672  			continue
   673  		}
   674  		if !withinTimeRange(ev, ctx.startTime, ctx.endTime) {
   675  			continue
   676  		}
   677  
   678  		if ev.P < trace.FakeP && ev.P > maxProc {
   679  			maxProc = ev.P
   680  		}
   681  
   682  		// Emit trace objects.
   683  		switch ev.Type {
   684  		case trace.EvProcStart:
   685  			if ctx.mode&modeGoroutineOriented != 0 {
   686  				continue
   687  			}
   688  			ctx.emitInstant(ev, "proc start", "")
   689  		case trace.EvProcStop:
   690  			if ctx.mode&modeGoroutineOriented != 0 {
   691  				continue
   692  			}
   693  			ctx.emitInstant(ev, "proc stop", "")
   694  		case trace.EvGCStart:
   695  			ctx.emitSlice(ev, "GC")
   696  		case trace.EvGCDone:
   697  		case trace.EvGCSTWStart:
   698  			if ctx.mode&modeGoroutineOriented != 0 {
   699  				continue
   700  			}
   701  			ctx.emitSlice(ev, fmt.Sprintf("STW (%s)", ev.SArgs[0]))
   702  		case trace.EvGCSTWDone:
   703  		case trace.EvGCMarkAssistStart:
   704  			// Mark assists can continue past preemptions, so truncate to the
   705  			// whichever comes first. We'll synthesize another slice if
   706  			// necessary in EvGoStart.
   707  			markFinish := ev.Link
   708  			goFinish := getGInfo(ev.G).start.Link
   709  			fakeMarkStart := *ev
   710  			text := "MARK ASSIST"
   711  			if markFinish == nil || markFinish.Ts > goFinish.Ts {
   712  				fakeMarkStart.Link = goFinish
   713  				text = "MARK ASSIST (unfinished)"
   714  			}
   715  			ctx.emitSlice(&fakeMarkStart, text)
   716  		case trace.EvGCSweepStart:
   717  			slice := ctx.makeSlice(ev, "SWEEP")
   718  			if done := ev.Link; done != nil && done.Args[0] != 0 {
   719  				slice.Arg = struct {
   720  					Swept     uint64 `json:"Swept bytes"`
   721  					Reclaimed uint64 `json:"Reclaimed bytes"`
   722  				}{done.Args[0], done.Args[1]}
   723  			}
   724  			ctx.emit(slice)
   725  		case trace.EvGoStart, trace.EvGoStartLabel:
   726  			info := getGInfo(ev.G)
   727  			if ev.Type == trace.EvGoStartLabel {
   728  				ctx.emitSlice(ev, ev.SArgs[0])
   729  			} else {
   730  				ctx.emitSlice(ev, info.name)
   731  			}
   732  			if info.markAssist != nil {
   733  				// If we're in a mark assist, synthesize a new slice, ending
   734  				// either when the mark assist ends or when we're descheduled.
   735  				markFinish := info.markAssist.Link
   736  				goFinish := ev.Link
   737  				fakeMarkStart := *ev
   738  				text := "MARK ASSIST (resumed, unfinished)"
   739  				if markFinish != nil && markFinish.Ts < goFinish.Ts {
   740  					fakeMarkStart.Link = markFinish
   741  					text = "MARK ASSIST (resumed)"
   742  				}
   743  				ctx.emitSlice(&fakeMarkStart, text)
   744  			}
   745  		case trace.EvGoCreate:
   746  			ctx.emitArrow(ev, "go")
   747  		case trace.EvGoUnblock:
   748  			ctx.emitArrow(ev, "unblock")
   749  		case trace.EvGoSysCall:
   750  			ctx.emitInstant(ev, "syscall", "")
   751  		case trace.EvGoSysExit:
   752  			ctx.emitArrow(ev, "sysexit")
   753  		case trace.EvUserLog:
   754  			ctx.emitInstant(ev, formatUserLog(ev), "user event")
   755  		case trace.EvUserTaskCreate:
   756  			ctx.emitInstant(ev, "task start", "user event")
   757  		case trace.EvUserTaskEnd:
   758  			ctx.emitInstant(ev, "task end", "user event")
   759  		}
   760  		// Emit any counter updates.
   761  		ctx.emitThreadCounters(ev)
   762  		ctx.emitHeapCounters(ev)
   763  		ctx.emitGoroutineCounters(ev)
   764  	}
   765  
   766  	ctx.emitSectionFooter(statsSection, "STATS", 0)
   767  
   768  	if ctx.mode&modeTaskOriented != 0 {
   769  		ctx.emitSectionFooter(tasksSection, "TASKS", 1)
   770  	}
   771  
   772  	if ctx.mode&modeGoroutineOriented != 0 {
   773  		ctx.emitSectionFooter(procsSection, "G", 2)
   774  	} else {
   775  		ctx.emitSectionFooter(procsSection, "PROCS", 2)
   776  	}
   777  
   778  	ctx.emitFooter(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: procsSection, Tid: trace.GCP, Arg: &NameArg{"GC"}})
   779  	ctx.emitFooter(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: procsSection, Tid: trace.GCP, Arg: &SortIndexArg{-6}})
   780  
   781  	ctx.emitFooter(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: procsSection, Tid: trace.NetpollP, Arg: &NameArg{"Network"}})
   782  	ctx.emitFooter(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: procsSection, Tid: trace.NetpollP, Arg: &SortIndexArg{-5}})
   783  
   784  	ctx.emitFooter(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: procsSection, Tid: trace.TimerP, Arg: &NameArg{"Timers"}})
   785  	ctx.emitFooter(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: procsSection, Tid: trace.TimerP, Arg: &SortIndexArg{-4}})
   786  
   787  	ctx.emitFooter(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: procsSection, Tid: trace.SyscallP, Arg: &NameArg{"Syscalls"}})
   788  	ctx.emitFooter(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: procsSection, Tid: trace.SyscallP, Arg: &SortIndexArg{-3}})
   789  
   790  	// Display rows for Ps if we are in the default trace view mode (not goroutine-oriented presentation)
   791  	if ctx.mode&modeGoroutineOriented == 0 {
   792  		for i := 0; i <= maxProc; i++ {
   793  			ctx.emitFooter(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: procsSection, Tid: uint64(i), Arg: &NameArg{fmt.Sprintf("Proc %v", i)}})
   794  			ctx.emitFooter(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: procsSection, Tid: uint64(i), Arg: &SortIndexArg{i}})
   795  		}
   796  	}
   797  
   798  	// Display task and its regions if we are in task-oriented presentation mode.
   799  	if ctx.mode&modeTaskOriented != 0 {
   800  		// sort tasks based on the task start time.
   801  		sortedTask := make([]*taskDesc, 0, len(ctx.tasks))
   802  		for _, task := range ctx.tasks {
   803  			sortedTask = append(sortedTask, task)
   804  		}
   805  		sort.SliceStable(sortedTask, func(i, j int) bool {
   806  			ti, tj := sortedTask[i], sortedTask[j]
   807  			if ti.firstTimestamp() == tj.firstTimestamp() {
   808  				return ti.lastTimestamp() < tj.lastTimestamp()
   809  			}
   810  			return ti.firstTimestamp() < tj.firstTimestamp()
   811  		})
   812  
   813  		for i, task := range sortedTask {
   814  			ctx.emitTask(task, i)
   815  
   816  			// If we are in goroutine-oriented mode, we draw regions.
   817  			// TODO(hyangah): add this for task/P-oriented mode (i.e., focustask view) too.
   818  			if ctx.mode&modeGoroutineOriented != 0 {
   819  				for _, s := range task.regions {
   820  					ctx.emitRegion(s)
   821  				}
   822  			}
   823  		}
   824  	}
   825  
   826  	// Display goroutine rows if we are either in goroutine-oriented mode.
   827  	if ctx.mode&modeGoroutineOriented != 0 {
   828  		for k, v := range ginfos {
   829  			if !ctx.gs[k] {
   830  				continue
   831  			}
   832  			ctx.emitFooter(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: procsSection, Tid: k, Arg: &NameArg{v.name}})
   833  		}
   834  		// Row for the main goroutine (maing)
   835  		ctx.emitFooter(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: procsSection, Tid: ctx.maing, Arg: &SortIndexArg{-2}})
   836  		// Row for GC or global state (specified with G=0)
   837  		ctx.emitFooter(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: procsSection, Tid: 0, Arg: &SortIndexArg{-1}})
   838  	}
   839  
   840  	return nil
   841  }
   842  
   843  func (ctx *traceContext) emit(e *ViewerEvent) {
   844  	ctx.consumer.consumeViewerEvent(e, false)
   845  }
   846  
   847  func (ctx *traceContext) emitFooter(e *ViewerEvent) {
   848  	ctx.consumer.consumeViewerEvent(e, true)
   849  }
   850  func (ctx *traceContext) emitSectionFooter(sectionID uint64, name string, priority int) {
   851  	ctx.emitFooter(&ViewerEvent{Name: "process_name", Phase: "M", Pid: sectionID, Arg: &NameArg{name}})
   852  	ctx.emitFooter(&ViewerEvent{Name: "process_sort_index", Phase: "M", Pid: sectionID, Arg: &SortIndexArg{priority}})
   853  }
   854  
   855  func (ctx *traceContext) time(ev *trace.Event) float64 {
   856  	// Trace viewer wants timestamps in microseconds.
   857  	return float64(ev.Ts) / 1000
   858  }
   859  
   860  func withinTimeRange(ev *trace.Event, s, e int64) bool {
   861  	if evEnd := ev.Link; evEnd != nil {
   862  		return ev.Ts <= e && evEnd.Ts >= s
   863  	}
   864  	return ev.Ts >= s && ev.Ts <= e
   865  }
   866  
   867  func tsWithinRange(ts, s, e int64) bool {
   868  	return s <= ts && ts <= e
   869  }
   870  
   871  func (ctx *traceContext) proc(ev *trace.Event) uint64 {
   872  	if ctx.mode&modeGoroutineOriented != 0 && ev.P < trace.FakeP {
   873  		return ev.G
   874  	} else {
   875  		return uint64(ev.P)
   876  	}
   877  }
   878  
   879  func (ctx *traceContext) emitSlice(ev *trace.Event, name string) {
   880  	ctx.emit(ctx.makeSlice(ev, name))
   881  }
   882  
   883  func (ctx *traceContext) makeSlice(ev *trace.Event, name string) *ViewerEvent {
   884  	// If ViewerEvent.Dur is not a positive value,
   885  	// trace viewer handles it as a non-terminating time interval.
   886  	// Avoid it by setting the field with a small value.
   887  	durationUsec := ctx.time(ev.Link) - ctx.time(ev)
   888  	if ev.Link.Ts-ev.Ts <= 0 {
   889  		durationUsec = 0.0001 // 0.1 nanoseconds
   890  	}
   891  	sl := &ViewerEvent{
   892  		Name:     name,
   893  		Phase:    "X",
   894  		Time:     ctx.time(ev),
   895  		Dur:      durationUsec,
   896  		Tid:      ctx.proc(ev),
   897  		Stack:    ctx.stack(ev.Stk),
   898  		EndStack: ctx.stack(ev.Link.Stk),
   899  	}
   900  
   901  	// grey out non-overlapping events if the event is not a global event (ev.G == 0)
   902  	if ctx.mode&modeTaskOriented != 0 && ev.G != 0 {
   903  		// include P information.
   904  		if t := ev.Type; t == trace.EvGoStart || t == trace.EvGoStartLabel {
   905  			type Arg struct {
   906  				P int
   907  			}
   908  			sl.Arg = &Arg{P: ev.P}
   909  		}
   910  		// grey out non-overlapping events.
   911  		overlapping := false
   912  		for _, task := range ctx.tasks {
   913  			if _, overlapped := task.overlappingDuration(ev); overlapped {
   914  				overlapping = true
   915  				break
   916  			}
   917  		}
   918  		if !overlapping {
   919  			sl.Cname = colorLightGrey
   920  		}
   921  	}
   922  	return sl
   923  }
   924  
   925  func (ctx *traceContext) emitTask(task *taskDesc, sortIndex int) {
   926  	taskRow := uint64(task.id)
   927  	taskName := task.name
   928  	durationUsec := float64(task.lastTimestamp()-task.firstTimestamp()) / 1e3
   929  
   930  	ctx.emitFooter(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: tasksSection, Tid: taskRow, Arg: &NameArg{fmt.Sprintf("T%d %s", task.id, taskName)}})
   931  	ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: tasksSection, Tid: taskRow, Arg: &SortIndexArg{sortIndex}})
   932  	ts := float64(task.firstTimestamp()) / 1e3
   933  	sl := &ViewerEvent{
   934  		Name:  taskName,
   935  		Phase: "X",
   936  		Time:  ts,
   937  		Dur:   durationUsec,
   938  		Pid:   tasksSection,
   939  		Tid:   taskRow,
   940  		Cname: pickTaskColor(task.id),
   941  	}
   942  	targ := TaskArg{ID: task.id}
   943  	if task.create != nil {
   944  		sl.Stack = ctx.stack(task.create.Stk)
   945  		targ.StartG = task.create.G
   946  	}
   947  	if task.end != nil {
   948  		sl.EndStack = ctx.stack(task.end.Stk)
   949  		targ.EndG = task.end.G
   950  	}
   951  	sl.Arg = targ
   952  	ctx.emit(sl)
   953  
   954  	if task.create != nil && task.create.Type == trace.EvUserTaskCreate && task.create.Args[1] != 0 {
   955  		ctx.arrowSeq++
   956  		ctx.emit(&ViewerEvent{Name: "newTask", Phase: "s", Tid: task.create.Args[1], ID: ctx.arrowSeq, Time: ts, Pid: tasksSection})
   957  		ctx.emit(&ViewerEvent{Name: "newTask", Phase: "t", Tid: taskRow, ID: ctx.arrowSeq, Time: ts, Pid: tasksSection})
   958  	}
   959  }
   960  
   961  func (ctx *traceContext) emitRegion(s regionDesc) {
   962  	if s.Name == "" {
   963  		return
   964  	}
   965  
   966  	if !tsWithinRange(s.firstTimestamp(), ctx.startTime, ctx.endTime) &&
   967  		!tsWithinRange(s.lastTimestamp(), ctx.startTime, ctx.endTime) {
   968  		return
   969  	}
   970  
   971  	ctx.regionID++
   972  	regionID := ctx.regionID
   973  
   974  	id := s.TaskID
   975  	scopeID := fmt.Sprintf("%x", id)
   976  	name := s.Name
   977  
   978  	sl0 := &ViewerEvent{
   979  		Category: "Region",
   980  		Name:     name,
   981  		Phase:    "b",
   982  		Time:     float64(s.firstTimestamp()) / 1e3,
   983  		Tid:      s.G, // only in goroutine-oriented view
   984  		ID:       uint64(regionID),
   985  		Scope:    scopeID,
   986  		Cname:    pickTaskColor(s.TaskID),
   987  	}
   988  	if s.Start != nil {
   989  		sl0.Stack = ctx.stack(s.Start.Stk)
   990  	}
   991  	ctx.emit(sl0)
   992  
   993  	sl1 := &ViewerEvent{
   994  		Category: "Region",
   995  		Name:     name,
   996  		Phase:    "e",
   997  		Time:     float64(s.lastTimestamp()) / 1e3,
   998  		Tid:      s.G,
   999  		ID:       uint64(regionID),
  1000  		Scope:    scopeID,
  1001  		Cname:    pickTaskColor(s.TaskID),
  1002  		Arg:      RegionArg{TaskID: s.TaskID},
  1003  	}
  1004  	if s.End != nil {
  1005  		sl1.Stack = ctx.stack(s.End.Stk)
  1006  	}
  1007  	ctx.emit(sl1)
  1008  }
  1009  
  1010  type heapCountersArg struct {
  1011  	Allocated uint64
  1012  	NextGC    uint64
  1013  }
  1014  
  1015  func (ctx *traceContext) emitHeapCounters(ev *trace.Event) {
  1016  	if ctx.prevHeapStats == ctx.heapStats {
  1017  		return
  1018  	}
  1019  	diff := uint64(0)
  1020  	if ctx.heapStats.nextGC > ctx.heapStats.heapAlloc {
  1021  		diff = ctx.heapStats.nextGC - ctx.heapStats.heapAlloc
  1022  	}
  1023  	if tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) {
  1024  		ctx.emit(&ViewerEvent{Name: "Heap", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &heapCountersArg{ctx.heapStats.heapAlloc, diff}})
  1025  	}
  1026  	ctx.prevHeapStats = ctx.heapStats
  1027  }
  1028  
  1029  type goroutineCountersArg struct {
  1030  	Running   uint64
  1031  	Runnable  uint64
  1032  	GCWaiting uint64
  1033  }
  1034  
  1035  func (ctx *traceContext) emitGoroutineCounters(ev *trace.Event) {
  1036  	if ctx.prevGstates == ctx.gstates {
  1037  		return
  1038  	}
  1039  	if tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) {
  1040  		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])}})
  1041  	}
  1042  	ctx.prevGstates = ctx.gstates
  1043  }
  1044  
  1045  type threadCountersArg struct {
  1046  	Running   int64
  1047  	InSyscall int64
  1048  }
  1049  
  1050  func (ctx *traceContext) emitThreadCounters(ev *trace.Event) {
  1051  	if ctx.prevThreadStats == ctx.threadStats {
  1052  		return
  1053  	}
  1054  	if tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) {
  1055  		ctx.emit(&ViewerEvent{Name: "Threads", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &threadCountersArg{
  1056  			Running:   ctx.threadStats.prunning,
  1057  			InSyscall: ctx.threadStats.insyscall}})
  1058  	}
  1059  	ctx.prevThreadStats = ctx.threadStats
  1060  }
  1061  
  1062  func (ctx *traceContext) emitInstant(ev *trace.Event, name, category string) {
  1063  	if !tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) {
  1064  		return
  1065  	}
  1066  
  1067  	cname := ""
  1068  	if ctx.mode&modeTaskOriented != 0 {
  1069  		taskID, isUserAnnotation := isUserAnnotationEvent(ev)
  1070  
  1071  		show := false
  1072  		for _, task := range ctx.tasks {
  1073  			if isUserAnnotation && task.id == taskID || task.overlappingInstant(ev) {
  1074  				show = true
  1075  				break
  1076  			}
  1077  		}
  1078  		// grey out or skip if non-overlapping instant.
  1079  		if !show {
  1080  			if isUserAnnotation {
  1081  				return // don't display unrelated user annotation events.
  1082  			}
  1083  			cname = colorLightGrey
  1084  		}
  1085  	}
  1086  	var arg interface{}
  1087  	if ev.Type == trace.EvProcStart {
  1088  		type Arg struct {
  1089  			ThreadID uint64
  1090  		}
  1091  		arg = &Arg{ev.Args[0]}
  1092  	}
  1093  	ctx.emit(&ViewerEvent{
  1094  		Name:     name,
  1095  		Category: category,
  1096  		Phase:    "I",
  1097  		Scope:    "t",
  1098  		Time:     ctx.time(ev),
  1099  		Tid:      ctx.proc(ev),
  1100  		Stack:    ctx.stack(ev.Stk),
  1101  		Cname:    cname,
  1102  		Arg:      arg})
  1103  }
  1104  
  1105  func (ctx *traceContext) emitArrow(ev *trace.Event, name string) {
  1106  	if ev.Link == nil {
  1107  		// The other end of the arrow is not captured in the trace.
  1108  		// For example, a goroutine was unblocked but was not scheduled before trace stop.
  1109  		return
  1110  	}
  1111  	if ctx.mode&modeGoroutineOriented != 0 && (!ctx.gs[ev.Link.G] || ev.Link.Ts < ctx.startTime || ev.Link.Ts > ctx.endTime) {
  1112  		return
  1113  	}
  1114  
  1115  	if ev.P == trace.NetpollP || ev.P == trace.TimerP || ev.P == trace.SyscallP {
  1116  		// Trace-viewer discards arrows if they don't start/end inside of a slice or instant.
  1117  		// So emit a fake instant at the start of the arrow.
  1118  		ctx.emitInstant(&trace.Event{P: ev.P, Ts: ev.Ts}, "unblock", "")
  1119  	}
  1120  
  1121  	color := ""
  1122  	if ctx.mode&modeTaskOriented != 0 {
  1123  		overlapping := false
  1124  		// skip non-overlapping arrows.
  1125  		for _, task := range ctx.tasks {
  1126  			if _, overlapped := task.overlappingDuration(ev); overlapped {
  1127  				overlapping = true
  1128  				break
  1129  			}
  1130  		}
  1131  		if !overlapping {
  1132  			return
  1133  		}
  1134  	}
  1135  
  1136  	ctx.arrowSeq++
  1137  	ctx.emit(&ViewerEvent{Name: name, Phase: "s", Tid: ctx.proc(ev), ID: ctx.arrowSeq, Time: ctx.time(ev), Stack: ctx.stack(ev.Stk), Cname: color})
  1138  	ctx.emit(&ViewerEvent{Name: name, Phase: "t", Tid: ctx.proc(ev.Link), ID: ctx.arrowSeq, Time: ctx.time(ev.Link), Cname: color})
  1139  }
  1140  
  1141  func (ctx *traceContext) stack(stk []*trace.Frame) int {
  1142  	return ctx.buildBranch(ctx.frameTree, stk)
  1143  }
  1144  
  1145  // buildBranch builds one branch in the prefix tree rooted at ctx.frameTree.
  1146  func (ctx *traceContext) buildBranch(parent frameNode, stk []*trace.Frame) int {
  1147  	if len(stk) == 0 {
  1148  		return parent.id
  1149  	}
  1150  	last := len(stk) - 1
  1151  	frame := stk[last]
  1152  	stk = stk[:last]
  1153  
  1154  	node, ok := parent.children[frame.PC]
  1155  	if !ok {
  1156  		ctx.frameSeq++
  1157  		node.id = ctx.frameSeq
  1158  		node.children = make(map[uint64]frameNode)
  1159  		parent.children[frame.PC] = node
  1160  		ctx.consumer.consumeViewerFrame(strconv.Itoa(node.id), ViewerFrame{fmt.Sprintf("%v:%v", frame.Fn, frame.Line), parent.id})
  1161  	}
  1162  	return ctx.buildBranch(node, stk)
  1163  }
  1164  
  1165  func isSystemGoroutine(entryFn string) bool {
  1166  	// This mimics runtime.isSystemGoroutine as closely as
  1167  	// possible.
  1168  	return entryFn != "runtime.main" && strings.HasPrefix(entryFn, "runtime.")
  1169  }
  1170  
  1171  // firstTimestamp returns the timestamp of the first event record.
  1172  func firstTimestamp() int64 {
  1173  	res, _ := parseTrace()
  1174  	if len(res.Events) > 0 {
  1175  		return res.Events[0].Ts
  1176  	}
  1177  	return 0
  1178  }
  1179  
  1180  // lastTimestamp returns the timestamp of the last event record.
  1181  func lastTimestamp() int64 {
  1182  	res, _ := parseTrace()
  1183  	if n := len(res.Events); n > 1 {
  1184  		return res.Events[n-1].Ts
  1185  	}
  1186  	return 0
  1187  }
  1188  
  1189  type jsonWriter struct {
  1190  	w   io.Writer
  1191  	enc *json.Encoder
  1192  }
  1193  
  1194  func viewerDataTraceConsumer(w io.Writer, start, end int64) traceConsumer {
  1195  	frames := make(map[string]ViewerFrame)
  1196  	enc := json.NewEncoder(w)
  1197  	written := 0
  1198  	index := int64(-1)
  1199  
  1200  	io.WriteString(w, "{")
  1201  	return traceConsumer{
  1202  		consumeTimeUnit: func(unit string) {
  1203  			io.WriteString(w, `"displayTimeUnit":`)
  1204  			enc.Encode(unit)
  1205  			io.WriteString(w, ",")
  1206  		},
  1207  		consumeViewerEvent: func(v *ViewerEvent, required bool) {
  1208  			index++
  1209  			if !required && (index < start || index > end) {
  1210  				// not in the range. Skip!
  1211  				return
  1212  			}
  1213  			if written == 0 {
  1214  				io.WriteString(w, `"traceEvents": [`)
  1215  			}
  1216  			if written > 0 {
  1217  				io.WriteString(w, ",")
  1218  			}
  1219  			enc.Encode(v)
  1220  			// TODO: get rid of the extra \n inserted by enc.Encode.
  1221  			// Same should be applied to splittingTraceConsumer.
  1222  			written++
  1223  		},
  1224  		consumeViewerFrame: func(k string, v ViewerFrame) {
  1225  			frames[k] = v
  1226  		},
  1227  		flush: func() {
  1228  			io.WriteString(w, `], "stackFrames":`)
  1229  			enc.Encode(frames)
  1230  			io.WriteString(w, `}`)
  1231  		},
  1232  	}
  1233  }
  1234  
  1235  // Mapping from more reasonable color names to the reserved color names in
  1236  // https://github.com/catapult-project/catapult/blob/master/tracing/tracing/base/color_scheme.html#L50
  1237  // The chrome trace viewer allows only those as cname values.
  1238  const (
  1239  	colorLightMauve     = "thread_state_uninterruptible" // 182, 125, 143
  1240  	colorOrange         = "thread_state_iowait"          // 255, 140, 0
  1241  	colorSeafoamGreen   = "thread_state_running"         // 126, 200, 148
  1242  	colorVistaBlue      = "thread_state_runnable"        // 133, 160, 210
  1243  	colorTan            = "thread_state_unknown"         // 199, 155, 125
  1244  	colorIrisBlue       = "background_memory_dump"       // 0, 180, 180
  1245  	colorMidnightBlue   = "light_memory_dump"            // 0, 0, 180
  1246  	colorDeepMagenta    = "detailed_memory_dump"         // 180, 0, 180
  1247  	colorBlue           = "vsync_highlight_color"        // 0, 0, 255
  1248  	colorGrey           = "generic_work"                 // 125, 125, 125
  1249  	colorGreen          = "good"                         // 0, 125, 0
  1250  	colorDarkGoldenrod  = "bad"                          // 180, 125, 0
  1251  	colorPeach          = "terrible"                     // 180, 0, 0
  1252  	colorBlack          = "black"                        // 0, 0, 0
  1253  	colorLightGrey      = "grey"                         // 221, 221, 221
  1254  	colorWhite          = "white"                        // 255, 255, 255
  1255  	colorYellow         = "yellow"                       // 255, 255, 0
  1256  	colorOlive          = "olive"                        // 100, 100, 0
  1257  	colorCornflowerBlue = "rail_response"                // 67, 135, 253
  1258  	colorSunsetOrange   = "rail_animation"               // 244, 74, 63
  1259  	colorTangerine      = "rail_idle"                    // 238, 142, 0
  1260  	colorShamrockGreen  = "rail_load"                    // 13, 168, 97
  1261  	colorGreenishYellow = "startup"                      // 230, 230, 0
  1262  	colorDarkGrey       = "heap_dump_stack_frame"        // 128, 128, 128
  1263  	colorTawny          = "heap_dump_child_node_arrow"   // 204, 102, 0
  1264  	colorLemon          = "cq_build_running"             // 255, 255, 119
  1265  	colorLime           = "cq_build_passed"              // 153, 238, 102
  1266  	colorPink           = "cq_build_failed"              // 238, 136, 136
  1267  	colorSilver         = "cq_build_abandoned"           // 187, 187, 187
  1268  	colorManzGreen      = "cq_build_attempt_runnig"      // 222, 222, 75
  1269  	colorKellyGreen     = "cq_build_attempt_passed"      // 108, 218, 35
  1270  	colorAnotherGrey    = "cq_build_attempt_failed"      // 187, 187, 187
  1271  )
  1272  
  1273  var colorForTask = []string{
  1274  	colorLightMauve,
  1275  	colorOrange,
  1276  	colorSeafoamGreen,
  1277  	colorVistaBlue,
  1278  	colorTan,
  1279  	colorMidnightBlue,
  1280  	colorIrisBlue,
  1281  	colorDeepMagenta,
  1282  	colorGreen,
  1283  	colorDarkGoldenrod,
  1284  	colorPeach,
  1285  	colorOlive,
  1286  	colorCornflowerBlue,
  1287  	colorSunsetOrange,
  1288  	colorTangerine,
  1289  	colorShamrockGreen,
  1290  	colorTawny,
  1291  	colorLemon,
  1292  	colorLime,
  1293  	colorPink,
  1294  	colorSilver,
  1295  	colorManzGreen,
  1296  	colorKellyGreen,
  1297  }
  1298  
  1299  func pickTaskColor(id uint64) string {
  1300  	idx := id % uint64(len(colorForTask))
  1301  	return colorForTask[idx]
  1302  }
  1303  

View as plain text