...
Run Format

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

View as plain text