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