| | |
| | |
| | |
| |
|
| | package main |
| |
|
| | import ( |
| | "fmt" |
| | "internal/trace" |
| | "internal/trace/traceviewer" |
| | "internal/trace/traceviewer/format" |
| | ) |
| |
|
| | var _ generator = &procGenerator{} |
| |
|
| | type procGenerator struct { |
| | globalRangeGenerator |
| | globalMetricGenerator |
| | procRangeGenerator |
| | stackSampleGenerator[trace.ProcID] |
| | logEventGenerator[trace.ProcID] |
| |
|
| | gStates map[trace.GoID]*gState[trace.ProcID] |
| | inSyscall map[trace.ProcID]*gState[trace.ProcID] |
| | maxProc trace.ProcID |
| | } |
| |
|
| | func newProcGenerator() *procGenerator { |
| | pg := new(procGenerator) |
| | rg := func(ev *trace.Event) trace.ProcID { |
| | return ev.Proc() |
| | } |
| | pg.stackSampleGenerator.getResource = rg |
| | pg.logEventGenerator.getResource = rg |
| | pg.gStates = make(map[trace.GoID]*gState[trace.ProcID]) |
| | pg.inSyscall = make(map[trace.ProcID]*gState[trace.ProcID]) |
| | return pg |
| | } |
| |
|
| | func (g *procGenerator) Sync() { |
| | g.globalRangeGenerator.Sync() |
| | g.procRangeGenerator.Sync() |
| | } |
| |
|
| | func (g *procGenerator) GoroutineLabel(ctx *traceContext, ev *trace.Event) { |
| | l := ev.Label() |
| | g.gStates[l.Resource.Goroutine()].setLabel(l.Label) |
| | } |
| |
|
| | func (g *procGenerator) GoroutineRange(ctx *traceContext, ev *trace.Event) { |
| | r := ev.Range() |
| | switch ev.Kind() { |
| | case trace.EventRangeBegin: |
| | g.gStates[r.Scope.Goroutine()].rangeBegin(ev.Time(), r.Name, ev.Stack()) |
| | case trace.EventRangeActive: |
| | g.gStates[r.Scope.Goroutine()].rangeActive(r.Name) |
| | case trace.EventRangeEnd: |
| | gs := g.gStates[r.Scope.Goroutine()] |
| | gs.rangeEnd(ev.Time(), r.Name, ev.Stack(), ctx) |
| | } |
| | } |
| |
|
| | func (g *procGenerator) GoroutineTransition(ctx *traceContext, ev *trace.Event) { |
| | st := ev.StateTransition() |
| | goID := st.Resource.Goroutine() |
| |
|
| | |
| | |
| | gs, ok := g.gStates[goID] |
| | if !ok { |
| | gs = newGState[trace.ProcID](goID) |
| | g.gStates[goID] = gs |
| | } |
| | |
| | gs.augmentName(st.Stack) |
| |
|
| | |
| | from, to := st.Goroutine() |
| | if from == to { |
| | |
| | return |
| | } |
| | if from == trace.GoRunning && !to.Executing() { |
| | if to == trace.GoWaiting { |
| | |
| | gs.block(ev.Time(), ev.Stack(), st.Reason, ctx) |
| | } else { |
| | gs.stop(ev.Time(), ev.Stack(), ctx) |
| | } |
| | } |
| | if !from.Executing() && to == trace.GoRunning { |
| | start := ev.Time() |
| | if from == trace.GoUndetermined { |
| | |
| | start = ctx.startTime |
| | } |
| | gs.start(start, ev.Proc(), ctx) |
| | } |
| |
|
| | if from == trace.GoWaiting { |
| | |
| | gs.unblock(ev.Time(), ev.Stack(), ev.Proc(), ctx) |
| | } |
| | if from == trace.GoNotExist && to == trace.GoRunnable { |
| | |
| | gs.created(ev.Time(), ev.Proc(), ev.Stack()) |
| | } |
| | if from == trace.GoSyscall && to != trace.GoRunning { |
| | |
| | gs.blockedSyscallEnd(ev.Time(), ev.Stack(), ctx) |
| | } |
| |
|
| | |
| | if to == trace.GoSyscall && ev.Proc() != trace.NoProc { |
| | start := ev.Time() |
| | if from == trace.GoUndetermined { |
| | |
| | start = ctx.startTime |
| | } |
| | |
| | |
| | |
| | gs.syscallBegin(start, ev.Proc(), ev.Stack()) |
| | g.inSyscall[ev.Proc()] = gs |
| | } |
| | |
| | _, didNotBlock := g.inSyscall[ev.Proc()] |
| | if from == trace.GoSyscall && didNotBlock { |
| | gs.syscallEnd(ev.Time(), false, ctx) |
| | delete(g.inSyscall, ev.Proc()) |
| | } |
| |
|
| | |
| | _, inMarkAssist := gs.activeRanges["GC mark assist"] |
| | ctx.GoroutineTransition(ctx.elapsed(ev.Time()), viewerGState(from, inMarkAssist), viewerGState(to, inMarkAssist)) |
| | } |
| |
|
| | func (g *procGenerator) ProcTransition(ctx *traceContext, ev *trace.Event) { |
| | st := ev.StateTransition() |
| | proc := st.Resource.Proc() |
| |
|
| | g.maxProc = max(g.maxProc, proc) |
| | viewerEv := traceviewer.InstantEvent{ |
| | Resource: uint64(proc), |
| | Stack: ctx.Stack(viewerFrames(ev.Stack())), |
| |
|
| | |
| | |
| | Arg: format.SchedCtxArg{ |
| | ProcID: uint64(st.Resource.Proc()), |
| | ThreadID: uint64(ev.Thread()), |
| | }, |
| | } |
| |
|
| | from, to := st.Proc() |
| | if from == to { |
| | |
| | return |
| | } |
| | if to.Executing() { |
| | start := ev.Time() |
| | if from == trace.ProcUndetermined { |
| | start = ctx.startTime |
| | } |
| | viewerEv.Name = "proc start" |
| | viewerEv.Ts = ctx.elapsed(start) |
| | ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, 1) |
| | } |
| | if from.Executing() { |
| | start := ev.Time() |
| | viewerEv.Name = "proc stop" |
| | viewerEv.Ts = ctx.elapsed(start) |
| | ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, -1) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | gs, ok := g.inSyscall[proc] |
| | if ok { |
| | |
| | gs.syscallEnd(start, true, ctx) |
| | gs.stop(start, ev.Stack(), ctx) |
| | delete(g.inSyscall, proc) |
| | } |
| | } |
| | |
| | |
| | |
| |
|
| | if viewerEv.Name != "" { |
| | ctx.Instant(viewerEv) |
| | } |
| | } |
| |
|
| | func (g *procGenerator) Finish(ctx *traceContext) { |
| | ctx.SetResourceType("PROCS") |
| |
|
| | |
| | |
| | |
| | g.procRangeGenerator.Finish(ctx) |
| | g.globalRangeGenerator.Finish(ctx) |
| |
|
| | |
| | for _, gs := range g.gStates { |
| | gs.finish(ctx) |
| | } |
| |
|
| | |
| | for i := uint64(0); i <= uint64(g.maxProc); i++ { |
| | ctx.Resource(i, fmt.Sprintf("Proc %v", i)) |
| | } |
| | } |
| |
|