Source file
src/cmd/trace/trace.go
1
2
3
4
5 package main
6
7 import (
8 "fmt"
9 "internal/trace"
10 "internal/trace/traceviewer"
11 "log"
12 "math"
13 "net/http"
14 "runtime/debug"
15 "sort"
16 "strconv"
17 "time"
18
19 "internal/trace/traceviewer/format"
20 )
21
22 func init() {
23 http.HandleFunc("/trace", httpTrace)
24 http.HandleFunc("/jsontrace", httpJsonTrace)
25 http.Handle("/static/", traceviewer.StaticHandler())
26 }
27
28
29 func httpTrace(w http.ResponseWriter, r *http.Request) {
30 _, err := parseTrace()
31 if err != nil {
32 http.Error(w, err.Error(), http.StatusInternalServerError)
33 return
34 }
35 traceviewer.TraceHandler().ServeHTTP(w, r)
36 }
37
38
39 func httpJsonTrace(w http.ResponseWriter, r *http.Request) {
40 defer debug.FreeOSMemory()
41 defer reportMemoryUsage("after httpJsonTrace")
42
43 res, err := parseTrace()
44 if err != nil {
45 log.Printf("failed to parse trace: %v", err)
46 return
47 }
48
49 params := &traceParams{
50 parsed: res,
51 endTime: math.MaxInt64,
52 }
53
54 if goids := r.FormValue("goid"); goids != "" {
55
56 goid, err := strconv.ParseUint(goids, 10, 64)
57 if err != nil {
58 log.Printf("failed to parse goid parameter %q: %v", goids, err)
59 return
60 }
61 analyzeGoroutines(res.Events)
62 g, ok := gs[goid]
63 if !ok {
64 log.Printf("failed to find goroutine %d", goid)
65 return
66 }
67 params.mode = traceviewer.ModeGoroutineOriented
68 params.startTime = g.StartTime
69 if g.EndTime != 0 {
70 params.endTime = g.EndTime
71 } else {
72 params.endTime = lastTimestamp()
73 }
74 params.maing = goid
75 params.gs = trace.RelatedGoroutines(res.Events, goid)
76 } else if taskids := r.FormValue("taskid"); taskids != "" {
77 taskid, err := strconv.ParseUint(taskids, 10, 64)
78 if err != nil {
79 log.Printf("failed to parse taskid parameter %q: %v", taskids, err)
80 return
81 }
82 annotRes, _ := analyzeAnnotations()
83 task, ok := annotRes.tasks[taskid]
84 if !ok || len(task.events) == 0 {
85 log.Printf("failed to find task with id %d", taskid)
86 return
87 }
88 goid := task.events[0].G
89 params.mode = traceviewer.ModeGoroutineOriented | traceviewer.ModeTaskOriented
90 params.startTime = task.firstTimestamp() - 1
91 params.endTime = task.lastTimestamp() + 1
92 params.maing = goid
93 params.tasks = task.descendants()
94 gs := map[uint64]bool{}
95 for _, t := range params.tasks {
96
97 for k, v := range t.RelatedGoroutines(res.Events, 0) {
98 gs[k] = v
99 }
100 }
101 params.gs = gs
102 } else if taskids := r.FormValue("focustask"); taskids != "" {
103 taskid, err := strconv.ParseUint(taskids, 10, 64)
104 if err != nil {
105 log.Printf("failed to parse focustask parameter %q: %v", taskids, err)
106 return
107 }
108 annotRes, _ := analyzeAnnotations()
109 task, ok := annotRes.tasks[taskid]
110 if !ok || len(task.events) == 0 {
111 log.Printf("failed to find task with id %d", taskid)
112 return
113 }
114 params.mode = traceviewer.ModeTaskOriented
115 params.startTime = task.firstTimestamp() - 1
116 params.endTime = task.lastTimestamp() + 1
117 params.tasks = task.descendants()
118 }
119
120 start := int64(0)
121 end := int64(math.MaxInt64)
122 if startStr, endStr := r.FormValue("start"), r.FormValue("end"); startStr != "" && endStr != "" {
123
124 start, err = strconv.ParseInt(startStr, 10, 64)
125 if err != nil {
126 log.Printf("failed to parse start parameter %q: %v", startStr, err)
127 return
128 }
129 end, err = strconv.ParseInt(endStr, 10, 64)
130 if err != nil {
131 log.Printf("failed to parse end parameter %q: %v", endStr, err)
132 return
133 }
134 }
135
136 c := traceviewer.ViewerDataTraceConsumer(w, start, end)
137 if err := generateTrace(params, c); err != nil {
138 log.Printf("failed to generate trace: %v", err)
139 return
140 }
141 }
142
143
144
145
146 func splitTrace(res trace.ParseResult) []traceviewer.Range {
147 params := &traceParams{
148 parsed: res,
149 endTime: math.MaxInt64,
150 }
151 s, c := traceviewer.SplittingTraceConsumer(100 << 20)
152 if err := generateTrace(params, c); err != nil {
153 dief("%v\n", err)
154 }
155 return s.Ranges
156 }
157
158 type traceParams struct {
159 parsed trace.ParseResult
160 mode traceviewer.Mode
161 startTime int64
162 endTime int64
163 maing uint64
164 gs map[uint64]bool
165 tasks []*taskDesc
166 }
167
168 type traceContext struct {
169 *traceParams
170 consumer traceviewer.TraceConsumer
171 emitter *traceviewer.Emitter
172 arrowSeq uint64
173 gcount uint64
174 regionID int
175 }
176
177 type gInfo struct {
178 state traceviewer.GState
179 name string
180 isSystemG bool
181 start *trace.Event
182 markAssist *trace.Event
183 }
184
185 type NameArg struct {
186 Name string `json:"name"`
187 }
188
189 type TaskArg struct {
190 ID uint64 `json:"id"`
191 StartG uint64 `json:"start_g,omitempty"`
192 EndG uint64 `json:"end_g,omitempty"`
193 }
194
195 type RegionArg struct {
196 TaskID uint64 `json:"taskid,omitempty"`
197 }
198
199 type SortIndexArg struct {
200 Index int `json:"sort_index"`
201 }
202
203
204
205
206
207
208
209
210 func generateTrace(params *traceParams, consumer traceviewer.TraceConsumer) error {
211 emitter := traceviewer.NewEmitter(
212 consumer,
213 time.Duration(params.startTime),
214 time.Duration(params.endTime),
215 )
216 if params.mode&traceviewer.ModeGoroutineOriented != 0 {
217 emitter.SetResourceType("G")
218 } else {
219 emitter.SetResourceType("PROCS")
220 }
221 defer emitter.Flush()
222
223 ctx := &traceContext{traceParams: params, emitter: emitter}
224 ctx.consumer = consumer
225
226 maxProc := 0
227 ginfos := make(map[uint64]*gInfo)
228 stacks := params.parsed.Stacks
229
230 getGInfo := func(g uint64) *gInfo {
231 info, ok := ginfos[g]
232 if !ok {
233 info = &gInfo{}
234 ginfos[g] = info
235 }
236 return info
237 }
238
239
240
241 var setGStateErr error
242 setGState := func(ev *trace.Event, g uint64, oldState, newState traceviewer.GState) {
243 info := getGInfo(g)
244 if oldState == traceviewer.GWaiting && info.state == traceviewer.GWaitingGC {
245
246 oldState = info.state
247 }
248 if info.state != oldState && setGStateErr == nil {
249 setGStateErr = fmt.Errorf("expected G %d to be in state %d, but got state %d", g, oldState, info.state)
250 }
251
252 emitter.GoroutineTransition(time.Duration(ev.Ts), info.state, newState)
253 info.state = newState
254 }
255
256 for _, ev := range ctx.parsed.Events {
257
258 switch ev.Type {
259 case trace.EvGoStart, trace.EvGoStartLabel:
260 setGState(ev, ev.G, traceviewer.GRunnable, traceviewer.GRunning)
261 info := getGInfo(ev.G)
262 info.start = ev
263 case trace.EvProcStart:
264 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateRunning, 1)
265 case trace.EvProcStop:
266 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateRunning, -1)
267 case trace.EvGoCreate:
268 newG := ev.Args[0]
269 info := getGInfo(newG)
270 if info.name != "" {
271 return fmt.Errorf("duplicate go create event for go id=%d detected at offset %d", newG, ev.Off)
272 }
273
274 stk, ok := stacks[ev.Args[1]]
275 if !ok || len(stk) == 0 {
276 return fmt.Errorf("invalid go create event: missing stack information for go id=%d at offset %d", newG, ev.Off)
277 }
278
279 fname := stk[0].Fn
280 info.name = fmt.Sprintf("G%v %s", newG, fname)
281 info.isSystemG = trace.IsSystemGoroutine(fname)
282
283 ctx.gcount++
284 setGState(ev, newG, traceviewer.GDead, traceviewer.GRunnable)
285 case trace.EvGoEnd:
286 ctx.gcount--
287 setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GDead)
288 case trace.EvGoUnblock:
289 setGState(ev, ev.Args[0], traceviewer.GWaiting, traceviewer.GRunnable)
290 case trace.EvGoSysExit:
291 setGState(ev, ev.G, traceviewer.GWaiting, traceviewer.GRunnable)
292 if getGInfo(ev.G).isSystemG {
293 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscallRuntime, -1)
294 } else {
295 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscall, -1)
296 }
297 case trace.EvGoSysBlock:
298 setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GWaiting)
299 if getGInfo(ev.G).isSystemG {
300 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscallRuntime, 1)
301 } else {
302 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscall, 1)
303 }
304 case trace.EvGoSched, trace.EvGoPreempt:
305 setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GRunnable)
306 case trace.EvGoStop,
307 trace.EvGoSleep, trace.EvGoBlock, trace.EvGoBlockSend, trace.EvGoBlockRecv,
308 trace.EvGoBlockSelect, trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockNet:
309 setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GWaiting)
310 case trace.EvGoBlockGC:
311 setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GWaitingGC)
312 case trace.EvGCMarkAssistStart:
313 getGInfo(ev.G).markAssist = ev
314 case trace.EvGCMarkAssistDone:
315 getGInfo(ev.G).markAssist = nil
316 case trace.EvGoWaiting:
317 setGState(ev, ev.G, traceviewer.GRunnable, traceviewer.GWaiting)
318 case trace.EvGoInSyscall:
319
320 setGState(ev, ev.G, traceviewer.GRunnable, traceviewer.GWaiting)
321 if getGInfo(ev.G).isSystemG {
322 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscallRuntime, 1)
323 } else {
324 emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscall, 1)
325 }
326 case trace.EvHeapAlloc:
327 emitter.HeapAlloc(time.Duration(ev.Ts), ev.Args[0])
328 case trace.EvHeapGoal:
329 emitter.HeapGoal(time.Duration(ev.Ts), ev.Args[0])
330 }
331 if setGStateErr != nil {
332 return setGStateErr
333 }
334
335 if err := emitter.Err(); err != nil {
336 return fmt.Errorf("invalid state after processing %v: %s", ev, err)
337 }
338
339
340
341 if ctx.gs != nil && ev.P < trace.FakeP && !ctx.gs[ev.G] {
342 continue
343 }
344 if !withinTimeRange(ev, ctx.startTime, ctx.endTime) {
345 continue
346 }
347
348 if ev.P < trace.FakeP && ev.P > maxProc {
349 maxProc = ev.P
350 }
351
352
353 switch ev.Type {
354 case trace.EvProcStart:
355 if ctx.mode&traceviewer.ModeGoroutineOriented != 0 {
356 continue
357 }
358 ctx.emitInstant(ev, "proc start", "")
359 case trace.EvProcStop:
360 if ctx.mode&traceviewer.ModeGoroutineOriented != 0 {
361 continue
362 }
363 ctx.emitInstant(ev, "proc stop", "")
364 case trace.EvGCStart:
365 ctx.emitSlice(ev, "GC")
366 case trace.EvGCDone:
367 case trace.EvSTWStart:
368 if ctx.mode&traceviewer.ModeGoroutineOriented != 0 {
369 continue
370 }
371 ctx.emitSlice(ev, fmt.Sprintf("STW (%s)", ev.SArgs[0]))
372 case trace.EvSTWDone:
373 case trace.EvGCMarkAssistStart:
374
375
376
377 markFinish := ev.Link
378 goFinish := getGInfo(ev.G).start.Link
379 fakeMarkStart := *ev
380 text := "MARK ASSIST"
381 if markFinish == nil || markFinish.Ts > goFinish.Ts {
382 fakeMarkStart.Link = goFinish
383 text = "MARK ASSIST (unfinished)"
384 }
385 ctx.emitSlice(&fakeMarkStart, text)
386 case trace.EvGCSweepStart:
387 slice := ctx.makeSlice(ev, "SWEEP")
388 if done := ev.Link; done != nil && done.Args[0] != 0 {
389 slice.Arg = struct {
390 Swept uint64 `json:"Swept bytes"`
391 Reclaimed uint64 `json:"Reclaimed bytes"`
392 }{done.Args[0], done.Args[1]}
393 }
394 ctx.emit(slice)
395 case trace.EvGoStart, trace.EvGoStartLabel:
396 info := getGInfo(ev.G)
397 if ev.Type == trace.EvGoStartLabel {
398 ctx.emitSlice(ev, ev.SArgs[0])
399 } else {
400 ctx.emitSlice(ev, info.name)
401 }
402 if info.markAssist != nil {
403
404
405 markFinish := info.markAssist.Link
406 goFinish := ev.Link
407 fakeMarkStart := *ev
408 text := "MARK ASSIST (resumed, unfinished)"
409 if markFinish != nil && markFinish.Ts < goFinish.Ts {
410 fakeMarkStart.Link = markFinish
411 text = "MARK ASSIST (resumed)"
412 }
413 ctx.emitSlice(&fakeMarkStart, text)
414 }
415 case trace.EvGoCreate:
416 ctx.emitArrow(ev, "go")
417 case trace.EvGoUnblock:
418 ctx.emitArrow(ev, "unblock")
419 case trace.EvGoSysCall:
420 ctx.emitInstant(ev, "syscall", "")
421 case trace.EvGoSysExit:
422 ctx.emitArrow(ev, "sysexit")
423 case trace.EvUserLog:
424 ctx.emitInstant(ev, formatUserLog(ev), "user event")
425 case trace.EvUserTaskCreate:
426 ctx.emitInstant(ev, "task start", "user event")
427 case trace.EvUserTaskEnd:
428 ctx.emitInstant(ev, "task end", "user event")
429 case trace.EvCPUSample:
430 if ev.P >= 0 {
431
432 ctx.emitInstant(ev, "CPU profile sample", "")
433 }
434 }
435 }
436
437
438 if ctx.mode&traceviewer.ModeTaskOriented != 0 {
439
440 sortedTask := make([]*taskDesc, len(ctx.tasks))
441 copy(sortedTask, ctx.tasks)
442 sort.SliceStable(sortedTask, func(i, j int) bool {
443 ti, tj := sortedTask[i], sortedTask[j]
444 if ti.firstTimestamp() == tj.firstTimestamp() {
445 return ti.lastTimestamp() < tj.lastTimestamp()
446 }
447 return ti.firstTimestamp() < tj.firstTimestamp()
448 })
449
450 for i, task := range sortedTask {
451 ctx.emitTask(task, i)
452
453
454
455 if ctx.mode&traceviewer.ModeGoroutineOriented != 0 {
456 for _, s := range task.regions {
457 ctx.emitRegion(s)
458 }
459 }
460 }
461 }
462
463
464 if ctx.mode&traceviewer.ModeGoroutineOriented != 0 {
465 for k, v := range ginfos {
466 if !ctx.gs[k] {
467 continue
468 }
469 emitter.Resource(k, v.name)
470 }
471 emitter.Focus(ctx.maing)
472
473
474 ctx.emitFooter(&format.Event{Name: "thread_sort_index", Phase: "M", PID: format.ProcsSection, TID: 0, Arg: &SortIndexArg{-1}})
475 } else {
476
477 for i := 0; i <= maxProc; i++ {
478 emitter.Resource(uint64(i), fmt.Sprintf("Proc %v", i))
479 }
480 }
481
482 return nil
483 }
484
485 func (ctx *traceContext) emit(e *format.Event) {
486 ctx.consumer.ConsumeViewerEvent(e, false)
487 }
488
489 func (ctx *traceContext) emitFooter(e *format.Event) {
490 ctx.consumer.ConsumeViewerEvent(e, true)
491 }
492 func (ctx *traceContext) time(ev *trace.Event) float64 {
493
494 return float64(ev.Ts) / 1000
495 }
496
497 func withinTimeRange(ev *trace.Event, s, e int64) bool {
498 if evEnd := ev.Link; evEnd != nil {
499 return ev.Ts <= e && evEnd.Ts >= s
500 }
501 return ev.Ts >= s && ev.Ts <= e
502 }
503
504 func tsWithinRange(ts, s, e int64) bool {
505 return s <= ts && ts <= e
506 }
507
508 func (ctx *traceContext) proc(ev *trace.Event) uint64 {
509 if ctx.mode&traceviewer.ModeGoroutineOriented != 0 && ev.P < trace.FakeP {
510 return ev.G
511 } else {
512 return uint64(ev.P)
513 }
514 }
515
516 func (ctx *traceContext) emitSlice(ev *trace.Event, name string) {
517 ctx.emit(ctx.makeSlice(ev, name))
518 }
519
520 func (ctx *traceContext) makeSlice(ev *trace.Event, name string) *format.Event {
521
522
523
524 durationUsec := ctx.time(ev.Link) - ctx.time(ev)
525 if ev.Link.Ts-ev.Ts <= 0 {
526 durationUsec = 0.0001
527 }
528 sl := &format.Event{
529 Name: name,
530 Phase: "X",
531 Time: ctx.time(ev),
532 Dur: durationUsec,
533 TID: ctx.proc(ev),
534 Stack: ctx.emitter.Stack(ev.Stk),
535 EndStack: ctx.emitter.Stack(ev.Link.Stk),
536 }
537
538
539 if ctx.mode&traceviewer.ModeTaskOriented != 0 && ev.G != 0 {
540
541 if t := ev.Type; t == trace.EvGoStart || t == trace.EvGoStartLabel {
542 type Arg struct {
543 P int
544 }
545 sl.Arg = &Arg{P: ev.P}
546 }
547
548 overlapping := false
549 for _, task := range ctx.tasks {
550 if _, overlapped := task.overlappingDuration(ev); overlapped {
551 overlapping = true
552 break
553 }
554 }
555 if !overlapping {
556 sl.Cname = colorLightGrey
557 }
558 }
559 return sl
560 }
561
562 func (ctx *traceContext) emitTask(task *taskDesc, sortIndex int) {
563 taskRow := uint64(task.id)
564 taskName := task.name
565 durationUsec := float64(task.lastTimestamp()-task.firstTimestamp()) / 1e3
566
567 ctx.emitter.Task(taskRow, taskName, sortIndex)
568 ts := float64(task.firstTimestamp()) / 1e3
569 sl := &format.Event{
570 Name: taskName,
571 Phase: "X",
572 Time: ts,
573 Dur: durationUsec,
574 PID: format.TasksSection,
575 TID: taskRow,
576 Cname: pickTaskColor(task.id),
577 }
578 targ := TaskArg{ID: task.id}
579 if task.create != nil {
580 sl.Stack = ctx.emitter.Stack(task.create.Stk)
581 targ.StartG = task.create.G
582 }
583 if task.end != nil {
584 sl.EndStack = ctx.emitter.Stack(task.end.Stk)
585 targ.EndG = task.end.G
586 }
587 sl.Arg = targ
588 ctx.emit(sl)
589
590 if task.create != nil && task.create.Type == trace.EvUserTaskCreate && task.create.Args[1] != 0 {
591 ctx.arrowSeq++
592 ctx.emit(&format.Event{Name: "newTask", Phase: "s", TID: task.create.Args[1], ID: ctx.arrowSeq, Time: ts, PID: format.TasksSection})
593 ctx.emit(&format.Event{Name: "newTask", Phase: "t", TID: taskRow, ID: ctx.arrowSeq, Time: ts, PID: format.TasksSection})
594 }
595 }
596
597 func (ctx *traceContext) emitRegion(s regionDesc) {
598 if s.Name == "" {
599 return
600 }
601
602 if !tsWithinRange(s.firstTimestamp(), ctx.startTime, ctx.endTime) &&
603 !tsWithinRange(s.lastTimestamp(), ctx.startTime, ctx.endTime) {
604 return
605 }
606
607 ctx.regionID++
608 regionID := ctx.regionID
609
610 id := s.TaskID
611 scopeID := fmt.Sprintf("%x", id)
612 name := s.Name
613
614 sl0 := &format.Event{
615 Category: "Region",
616 Name: name,
617 Phase: "b",
618 Time: float64(s.firstTimestamp()) / 1e3,
619 TID: s.G,
620 ID: uint64(regionID),
621 Scope: scopeID,
622 Cname: pickTaskColor(s.TaskID),
623 }
624 if s.Start != nil {
625 sl0.Stack = ctx.emitter.Stack(s.Start.Stk)
626 }
627 ctx.emit(sl0)
628
629 sl1 := &format.Event{
630 Category: "Region",
631 Name: name,
632 Phase: "e",
633 Time: float64(s.lastTimestamp()) / 1e3,
634 TID: s.G,
635 ID: uint64(regionID),
636 Scope: scopeID,
637 Cname: pickTaskColor(s.TaskID),
638 Arg: RegionArg{TaskID: s.TaskID},
639 }
640 if s.End != nil {
641 sl1.Stack = ctx.emitter.Stack(s.End.Stk)
642 }
643 ctx.emit(sl1)
644 }
645
646 func (ctx *traceContext) emitInstant(ev *trace.Event, name, category string) {
647 if !tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) {
648 return
649 }
650
651 cname := ""
652 if ctx.mode&traceviewer.ModeTaskOriented != 0 {
653 taskID, isUserAnnotation := isUserAnnotationEvent(ev)
654
655 show := false
656 for _, task := range ctx.tasks {
657 if isUserAnnotation && task.id == taskID || task.overlappingInstant(ev) {
658 show = true
659 break
660 }
661 }
662
663 if !show {
664 if isUserAnnotation {
665 return
666 }
667 cname = colorLightGrey
668 }
669 }
670 var arg any
671 if ev.Type == trace.EvProcStart {
672 type Arg struct {
673 ThreadID uint64
674 }
675 arg = &Arg{ev.Args[0]}
676 }
677 ctx.emit(&format.Event{
678 Name: name,
679 Category: category,
680 Phase: "I",
681 Scope: "t",
682 Time: ctx.time(ev),
683 TID: ctx.proc(ev),
684 Stack: ctx.emitter.Stack(ev.Stk),
685 Cname: cname,
686 Arg: arg})
687 }
688
689 func (ctx *traceContext) emitArrow(ev *trace.Event, name string) {
690 if ev.Link == nil {
691
692
693 return
694 }
695 if ctx.mode&traceviewer.ModeGoroutineOriented != 0 && (!ctx.gs[ev.Link.G] || ev.Link.Ts < ctx.startTime || ev.Link.Ts > ctx.endTime) {
696 return
697 }
698
699 if ev.P == trace.NetpollP || ev.P == trace.TimerP || ev.P == trace.SyscallP {
700
701
702 ctx.emitInstant(&trace.Event{P: ev.P, Ts: ev.Ts}, "unblock", "")
703 }
704
705 color := ""
706 if ctx.mode&traceviewer.ModeTaskOriented != 0 {
707 overlapping := false
708
709 for _, task := range ctx.tasks {
710 if _, overlapped := task.overlappingDuration(ev); overlapped {
711 overlapping = true
712 break
713 }
714 }
715 if !overlapping {
716 return
717 }
718 }
719
720 ctx.arrowSeq++
721 ctx.emit(&format.Event{Name: name, Phase: "s", TID: ctx.proc(ev), ID: ctx.arrowSeq, Time: ctx.time(ev), Stack: ctx.emitter.Stack(ev.Stk), Cname: color})
722 ctx.emit(&format.Event{Name: name, Phase: "t", TID: ctx.proc(ev.Link), ID: ctx.arrowSeq, Time: ctx.time(ev.Link), Cname: color})
723 }
724
725
726 func firstTimestamp() int64 {
727 res, _ := parseTrace()
728 if len(res.Events) > 0 {
729 return res.Events[0].Ts
730 }
731 return 0
732 }
733
734
735 func lastTimestamp() int64 {
736 res, _ := parseTrace()
737 if n := len(res.Events); n > 1 {
738 return res.Events[n-1].Ts
739 }
740 return 0
741 }
742
743
744
745
746 const (
747 colorLightMauve = "thread_state_uninterruptible"
748 colorOrange = "thread_state_iowait"
749 colorSeafoamGreen = "thread_state_running"
750 colorVistaBlue = "thread_state_runnable"
751 colorTan = "thread_state_unknown"
752 colorIrisBlue = "background_memory_dump"
753 colorMidnightBlue = "light_memory_dump"
754 colorDeepMagenta = "detailed_memory_dump"
755 colorBlue = "vsync_highlight_color"
756 colorGrey = "generic_work"
757 colorGreen = "good"
758 colorDarkGoldenrod = "bad"
759 colorPeach = "terrible"
760 colorBlack = "black"
761 colorLightGrey = "grey"
762 colorWhite = "white"
763 colorYellow = "yellow"
764 colorOlive = "olive"
765 colorCornflowerBlue = "rail_response"
766 colorSunsetOrange = "rail_animation"
767 colorTangerine = "rail_idle"
768 colorShamrockGreen = "rail_load"
769 colorGreenishYellow = "startup"
770 colorDarkGrey = "heap_dump_stack_frame"
771 colorTawny = "heap_dump_child_node_arrow"
772 colorLemon = "cq_build_running"
773 colorLime = "cq_build_passed"
774 colorPink = "cq_build_failed"
775 colorSilver = "cq_build_abandoned"
776 colorManzGreen = "cq_build_attempt_runnig"
777 colorKellyGreen = "cq_build_attempt_passed"
778 colorAnotherGrey = "cq_build_attempt_failed"
779 )
780
781 var colorForTask = []string{
782 colorLightMauve,
783 colorOrange,
784 colorSeafoamGreen,
785 colorVistaBlue,
786 colorTan,
787 colorMidnightBlue,
788 colorIrisBlue,
789 colorDeepMagenta,
790 colorGreen,
791 colorDarkGoldenrod,
792 colorPeach,
793 colorOlive,
794 colorCornflowerBlue,
795 colorSunsetOrange,
796 colorTangerine,
797 colorShamrockGreen,
798 colorTawny,
799 colorLemon,
800 colorLime,
801 colorPink,
802 colorSilver,
803 colorManzGreen,
804 colorKellyGreen,
805 }
806
807 func pickTaskColor(id uint64) string {
808 idx := id % uint64(len(colorForTask))
809 return colorForTask[idx]
810 }
811
View as plain text