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