github.com/MangoDowner/go-gm@v0.0.0-20180818020936-8baa2bd4408c/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 gInfo struct { 292 state gState // current state 293 name string // name chosen for this goroutine at first EvGoStart 294 start *trace.Event // most recent EvGoStart 295 markAssist *trace.Event // if non-nil, the mark assist currently running. 296 } 297 298 type ViewerData struct { 299 Events []*ViewerEvent `json:"traceEvents"` 300 Frames map[string]ViewerFrame `json:"stackFrames"` 301 TimeUnit string `json:"displayTimeUnit"` 302 303 // This is where mandatory part of the trace starts (e.g. thread names) 304 footer int 305 } 306 307 type ViewerEvent struct { 308 Name string `json:"name,omitempty"` 309 Phase string `json:"ph"` 310 Scope string `json:"s,omitempty"` 311 Time float64 `json:"ts"` 312 Dur float64 `json:"dur,omitempty"` 313 Pid uint64 `json:"pid"` 314 Tid uint64 `json:"tid"` 315 ID uint64 `json:"id,omitempty"` 316 Stack int `json:"sf,omitempty"` 317 EndStack int `json:"esf,omitempty"` 318 Arg interface{} `json:"args,omitempty"` 319 } 320 321 type ViewerFrame struct { 322 Name string `json:"name"` 323 Parent int `json:"parent,omitempty"` 324 } 325 326 type NameArg struct { 327 Name string `json:"name"` 328 } 329 330 type SortIndexArg struct { 331 Index int `json:"sort_index"` 332 } 333 334 // generateTrace generates json trace for trace-viewer: 335 // https://github.com/google/trace-viewer 336 // Trace format is described at: 337 // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/view 338 // If gtrace=true, generate trace for goroutine goid, otherwise whole trace. 339 // startTime, endTime determine part of the trace that we are interested in. 340 // gset restricts goroutines that are included in the resulting trace. 341 func generateTrace(params *traceParams) (ViewerData, error) { 342 ctx := &traceContext{traceParams: params} 343 ctx.frameTree.children = make(map[uint64]frameNode) 344 ctx.data.Frames = make(map[string]ViewerFrame) 345 ctx.data.TimeUnit = "ns" 346 maxProc := 0 347 ginfos := make(map[uint64]*gInfo) 348 349 getGInfo := func(g uint64) *gInfo { 350 info, ok := ginfos[g] 351 if !ok { 352 info = &gInfo{} 353 ginfos[g] = info 354 } 355 return info 356 } 357 358 // Since we make many calls to setGState, we record a sticky 359 // error in setGStateErr and check it after every event. 360 var setGStateErr error 361 setGState := func(ev *trace.Event, g uint64, oldState, newState gState) { 362 info := getGInfo(g) 363 if oldState == gWaiting && info.state == gWaitingGC { 364 // For checking, gWaiting counts as any gWaiting*. 365 oldState = info.state 366 } 367 if info.state != oldState && setGStateErr == nil { 368 setGStateErr = fmt.Errorf("expected G %d to be in state %d, but got state %d", g, oldState, newState) 369 } 370 ctx.gstates[info.state]-- 371 ctx.gstates[newState]++ 372 info.state = newState 373 } 374 for _, ev := range ctx.events { 375 // Handle state transitions before we filter out events. 376 switch ev.Type { 377 case trace.EvGoStart, trace.EvGoStartLabel: 378 setGState(ev, ev.G, gRunnable, gRunning) 379 info := getGInfo(ev.G) 380 if info.name == "" { 381 if len(ev.Stk) > 0 { 382 info.name = fmt.Sprintf("G%v %s", ev.G, ev.Stk[0].Fn) 383 } else { 384 info.name = fmt.Sprintf("G%v", ev.G) 385 } 386 } 387 info.start = ev 388 case trace.EvProcStart: 389 ctx.threadStats.prunning++ 390 case trace.EvProcStop: 391 ctx.threadStats.prunning-- 392 case trace.EvGoCreate: 393 ctx.gcount++ 394 setGState(ev, ev.Args[0], gDead, gRunnable) 395 case trace.EvGoEnd: 396 ctx.gcount-- 397 setGState(ev, ev.G, gRunning, gDead) 398 case trace.EvGoUnblock: 399 setGState(ev, ev.Args[0], gWaiting, gRunnable) 400 case trace.EvGoSysExit: 401 setGState(ev, ev.G, gWaiting, gRunnable) 402 ctx.threadStats.insyscall-- 403 case trace.EvGoSysBlock: 404 setGState(ev, ev.G, gRunning, gWaiting) 405 ctx.threadStats.insyscall++ 406 case trace.EvGoSched, trace.EvGoPreempt: 407 setGState(ev, ev.G, gRunning, gRunnable) 408 case trace.EvGoStop, 409 trace.EvGoSleep, trace.EvGoBlock, trace.EvGoBlockSend, trace.EvGoBlockRecv, 410 trace.EvGoBlockSelect, trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockNet: 411 setGState(ev, ev.G, gRunning, gWaiting) 412 case trace.EvGoBlockGC: 413 setGState(ev, ev.G, gRunning, gWaitingGC) 414 case trace.EvGCMarkAssistStart: 415 getGInfo(ev.G).markAssist = ev 416 case trace.EvGCMarkAssistDone: 417 getGInfo(ev.G).markAssist = nil 418 case trace.EvGoWaiting: 419 setGState(ev, ev.G, gRunnable, gWaiting) 420 case trace.EvGoInSyscall: 421 // Cancel out the effect of EvGoCreate at the beginning. 422 setGState(ev, ev.G, gRunnable, gWaiting) 423 ctx.threadStats.insyscall++ 424 case trace.EvHeapAlloc: 425 ctx.heapStats.heapAlloc = ev.Args[0] 426 case trace.EvNextGC: 427 ctx.heapStats.nextGC = ev.Args[0] 428 } 429 if setGStateErr != nil { 430 return ctx.data, setGStateErr 431 } 432 if ctx.gstates[gRunnable] < 0 || ctx.gstates[gRunning] < 0 || ctx.threadStats.insyscall < 0 { 433 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) 434 } 435 436 // Ignore events that are from uninteresting goroutines 437 // or outside of the interesting timeframe. 438 if ctx.gs != nil && ev.P < trace.FakeP && !ctx.gs[ev.G] { 439 continue 440 } 441 if ev.Ts < ctx.startTime || ev.Ts > ctx.endTime { 442 continue 443 } 444 445 if ev.P < trace.FakeP && ev.P > maxProc { 446 maxProc = ev.P 447 } 448 449 // Emit trace objects. 450 switch ev.Type { 451 case trace.EvProcStart: 452 if ctx.gtrace { 453 continue 454 } 455 ctx.emitInstant(ev, "proc start") 456 case trace.EvProcStop: 457 if ctx.gtrace { 458 continue 459 } 460 ctx.emitInstant(ev, "proc stop") 461 case trace.EvGCStart: 462 ctx.emitSlice(ev, "GC") 463 case trace.EvGCDone: 464 case trace.EvGCScanStart: 465 if ctx.gtrace { 466 continue 467 } 468 ctx.emitSlice(ev, "MARK TERMINATION") 469 case trace.EvGCScanDone: 470 case trace.EvGCMarkAssistStart: 471 // Mark assists can continue past preemptions, so truncate to the 472 // whichever comes first. We'll synthesize another slice if 473 // necessary in EvGoStart. 474 markFinish := ev.Link 475 goFinish := getGInfo(ev.G).start.Link 476 fakeMarkStart := *ev 477 text := "MARK ASSIST" 478 if markFinish == nil || markFinish.Ts > goFinish.Ts { 479 fakeMarkStart.Link = goFinish 480 text = "MARK ASSIST (unfinished)" 481 } 482 ctx.emitSlice(&fakeMarkStart, text) 483 case trace.EvGCSweepStart: 484 slice := ctx.emitSlice(ev, "SWEEP") 485 if done := ev.Link; done != nil && done.Args[0] != 0 { 486 slice.Arg = struct { 487 Swept uint64 `json:"Swept bytes"` 488 Reclaimed uint64 `json:"Reclaimed bytes"` 489 }{done.Args[0], done.Args[1]} 490 } 491 case trace.EvGoStart, trace.EvGoStartLabel: 492 info := getGInfo(ev.G) 493 if ev.Type == trace.EvGoStartLabel { 494 ctx.emitSlice(ev, ev.SArgs[0]) 495 } else { 496 ctx.emitSlice(ev, info.name) 497 } 498 if info.markAssist != nil { 499 // If we're in a mark assist, synthesize a new slice, ending 500 // either when the mark assist ends or when we're descheduled. 501 markFinish := info.markAssist.Link 502 goFinish := ev.Link 503 fakeMarkStart := *ev 504 text := "MARK ASSIST (resumed, unfinished)" 505 if markFinish != nil && markFinish.Ts < goFinish.Ts { 506 fakeMarkStart.Link = markFinish 507 text = "MARK ASSIST (resumed)" 508 } 509 ctx.emitSlice(&fakeMarkStart, text) 510 } 511 case trace.EvGoCreate: 512 ctx.emitArrow(ev, "go") 513 case trace.EvGoUnblock: 514 ctx.emitArrow(ev, "unblock") 515 case trace.EvGoSysCall: 516 ctx.emitInstant(ev, "syscall") 517 case trace.EvGoSysExit: 518 ctx.emitArrow(ev, "sysexit") 519 } 520 // Emit any counter updates. 521 ctx.emitThreadCounters(ev) 522 ctx.emitHeapCounters(ev) 523 ctx.emitGoroutineCounters(ev) 524 } 525 526 ctx.data.footer = len(ctx.data.Events) 527 ctx.emit(&ViewerEvent{Name: "process_name", Phase: "M", Pid: 0, Arg: &NameArg{"PROCS"}}) 528 ctx.emit(&ViewerEvent{Name: "process_sort_index", Phase: "M", Pid: 0, Arg: &SortIndexArg{1}}) 529 530 ctx.emit(&ViewerEvent{Name: "process_name", Phase: "M", Pid: 1, Arg: &NameArg{"STATS"}}) 531 ctx.emit(&ViewerEvent{Name: "process_sort_index", Phase: "M", Pid: 1, Arg: &SortIndexArg{0}}) 532 533 ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.GCP, Arg: &NameArg{"GC"}}) 534 ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.GCP, Arg: &SortIndexArg{-6}}) 535 536 ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.NetpollP, Arg: &NameArg{"Network"}}) 537 ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.NetpollP, Arg: &SortIndexArg{-5}}) 538 539 ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.TimerP, Arg: &NameArg{"Timers"}}) 540 ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.TimerP, Arg: &SortIndexArg{-4}}) 541 542 ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.SyscallP, Arg: &NameArg{"Syscalls"}}) 543 ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.SyscallP, Arg: &SortIndexArg{-3}}) 544 545 if !ctx.gtrace { 546 for i := 0; i <= maxProc; i++ { 547 ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: uint64(i), Arg: &NameArg{fmt.Sprintf("Proc %v", i)}}) 548 ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: uint64(i), Arg: &SortIndexArg{i}}) 549 } 550 } 551 552 if ctx.gtrace && ctx.gs != nil { 553 for k, v := range ginfos { 554 if !ctx.gs[k] { 555 continue 556 } 557 ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: k, Arg: &NameArg{v.name}}) 558 } 559 ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: ctx.maing, Arg: &SortIndexArg{-2}}) 560 ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: 0, Arg: &SortIndexArg{-1}}) 561 } 562 563 return ctx.data, nil 564 } 565 566 func (ctx *traceContext) emit(e *ViewerEvent) { 567 ctx.data.Events = append(ctx.data.Events, e) 568 } 569 570 func (ctx *traceContext) time(ev *trace.Event) float64 { 571 // Trace viewer wants timestamps in microseconds. 572 return float64(ev.Ts-ctx.startTime) / 1000 573 } 574 575 func (ctx *traceContext) proc(ev *trace.Event) uint64 { 576 if ctx.gtrace && ev.P < trace.FakeP { 577 return ev.G 578 } else { 579 return uint64(ev.P) 580 } 581 } 582 583 func (ctx *traceContext) emitSlice(ev *trace.Event, name string) *ViewerEvent { 584 sl := &ViewerEvent{ 585 Name: name, 586 Phase: "X", 587 Time: ctx.time(ev), 588 Dur: ctx.time(ev.Link) - ctx.time(ev), 589 Tid: ctx.proc(ev), 590 Stack: ctx.stack(ev.Stk), 591 EndStack: ctx.stack(ev.Link.Stk), 592 } 593 ctx.emit(sl) 594 return sl 595 } 596 597 type heapCountersArg struct { 598 Allocated uint64 599 NextGC uint64 600 } 601 602 func (ctx *traceContext) emitHeapCounters(ev *trace.Event) { 603 if ctx.gtrace { 604 return 605 } 606 if ctx.prevHeapStats == ctx.heapStats { 607 return 608 } 609 diff := uint64(0) 610 if ctx.heapStats.nextGC > ctx.heapStats.heapAlloc { 611 diff = ctx.heapStats.nextGC - ctx.heapStats.heapAlloc 612 } 613 ctx.emit(&ViewerEvent{Name: "Heap", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &heapCountersArg{ctx.heapStats.heapAlloc, diff}}) 614 ctx.prevHeapStats = ctx.heapStats 615 } 616 617 type goroutineCountersArg struct { 618 Running uint64 619 Runnable uint64 620 GCWaiting uint64 621 } 622 623 func (ctx *traceContext) emitGoroutineCounters(ev *trace.Event) { 624 if ctx.gtrace { 625 return 626 } 627 if ctx.prevGstates == ctx.gstates { 628 return 629 } 630 ctx.emit(&ViewerEvent{Name: "Goroutines", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &goroutineCountersArg{ctx.gstates[gRunning], ctx.gstates[gRunnable], ctx.gstates[gWaitingGC]}}) 631 ctx.prevGstates = ctx.gstates 632 } 633 634 type threadCountersArg struct { 635 Running uint64 636 InSyscall uint64 637 } 638 639 func (ctx *traceContext) emitThreadCounters(ev *trace.Event) { 640 if ctx.gtrace { 641 return 642 } 643 if ctx.prevThreadStats == ctx.threadStats { 644 return 645 } 646 ctx.emit(&ViewerEvent{Name: "Threads", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &threadCountersArg{ctx.threadStats.prunning, ctx.threadStats.insyscall}}) 647 ctx.prevThreadStats = ctx.threadStats 648 } 649 650 func (ctx *traceContext) emitInstant(ev *trace.Event, name string) { 651 var arg interface{} 652 if ev.Type == trace.EvProcStart { 653 type Arg struct { 654 ThreadID uint64 655 } 656 arg = &Arg{ev.Args[0]} 657 } 658 ctx.emit(&ViewerEvent{Name: name, Phase: "I", Scope: "t", Time: ctx.time(ev), Tid: ctx.proc(ev), Stack: ctx.stack(ev.Stk), Arg: arg}) 659 } 660 661 func (ctx *traceContext) emitArrow(ev *trace.Event, name string) { 662 if ev.Link == nil { 663 // The other end of the arrow is not captured in the trace. 664 // For example, a goroutine was unblocked but was not scheduled before trace stop. 665 return 666 } 667 if ctx.gtrace && (!ctx.gs[ev.Link.G] || ev.Link.Ts < ctx.startTime || ev.Link.Ts > ctx.endTime) { 668 return 669 } 670 671 if ev.P == trace.NetpollP || ev.P == trace.TimerP || ev.P == trace.SyscallP { 672 // Trace-viewer discards arrows if they don't start/end inside of a slice or instant. 673 // So emit a fake instant at the start of the arrow. 674 ctx.emitInstant(&trace.Event{P: ev.P, Ts: ev.Ts}, "unblock") 675 } 676 677 ctx.arrowSeq++ 678 ctx.emit(&ViewerEvent{Name: name, Phase: "s", Tid: ctx.proc(ev), ID: ctx.arrowSeq, Time: ctx.time(ev), Stack: ctx.stack(ev.Stk)}) 679 ctx.emit(&ViewerEvent{Name: name, Phase: "t", Tid: ctx.proc(ev.Link), ID: ctx.arrowSeq, Time: ctx.time(ev.Link)}) 680 } 681 682 func (ctx *traceContext) stack(stk []*trace.Frame) int { 683 return ctx.buildBranch(ctx.frameTree, stk) 684 } 685 686 // buildBranch builds one branch in the prefix tree rooted at ctx.frameTree. 687 func (ctx *traceContext) buildBranch(parent frameNode, stk []*trace.Frame) int { 688 if len(stk) == 0 { 689 return parent.id 690 } 691 last := len(stk) - 1 692 frame := stk[last] 693 stk = stk[:last] 694 695 node, ok := parent.children[frame.PC] 696 if !ok { 697 ctx.frameSeq++ 698 node.id = ctx.frameSeq 699 node.children = make(map[uint64]frameNode) 700 parent.children[frame.PC] = node 701 ctx.data.Frames[strconv.Itoa(node.id)] = ViewerFrame{fmt.Sprintf("%v:%v", frame.Fn, frame.Line), parent.id} 702 } 703 return ctx.buildBranch(node, stk) 704 }