github.com/freddyisaac/sicortex-golang@v0.0.0-20231019035217-e03519e66f60/src/cmd/trace/trace.go (about)

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