diff --git a/src/cmd/trace/trace.go b/src/cmd/trace/trace.go index d69c5feebc..45d14f873d 100644 --- a/src/cmd/trace/trace.go +++ b/src/cmd/trace/trace.go @@ -287,8 +287,9 @@ type heapStats struct { } type threadStats struct { - insyscall uint64 - prunning uint64 + insyscallRuntime uint64 // system goroutine in syscall + insyscall uint64 // user goroutine in syscall + prunning uint64 // thread running P } type frameNode struct { @@ -309,8 +310,9 @@ const ( ) type gInfo struct { - state gState // current state - name string // name chosen for this goroutine at first EvGoStart + state gState // current state + name string // name chosen for this goroutine at first EvGoStart + isSystemG bool start *trace.Event // most recent EvGoStart markAssist *trace.Event // if non-nil, the mark assist currently running. } @@ -391,6 +393,7 @@ func generateTrace(params *traceParams) (ViewerData, error) { ctx.gstates[newState]++ info.state = newState } + for _, ev := range ctx.events { // Handle state transitions before we filter out events. switch ev.Type { @@ -398,10 +401,12 @@ func generateTrace(params *traceParams) (ViewerData, error) { setGState(ev, ev.G, gRunnable, gRunning) info := getGInfo(ev.G) if info.name == "" { - if len(ev.Stk) > 0 { - info.name = fmt.Sprintf("G%v %s", ev.G, ev.Stk[0].Fn) - } else { + if len(ev.Stk) == 0 { info.name = fmt.Sprintf("G%v", ev.G) + } else { + fname := ev.Stk[0].Fn + info.name = fmt.Sprintf("G%v %s", ev.G, fname) + info.isSystemG = strings.HasPrefix(fname, "runtime.") && fname != "runtime.main" } } info.start = ev @@ -419,10 +424,18 @@ func generateTrace(params *traceParams) (ViewerData, error) { setGState(ev, ev.Args[0], gWaiting, gRunnable) case trace.EvGoSysExit: setGState(ev, ev.G, gWaiting, gRunnable) - ctx.threadStats.insyscall-- + if getGInfo(ev.G).isSystemG { + ctx.threadStats.insyscallRuntime-- + } else { + ctx.threadStats.insyscall-- + } case trace.EvGoSysBlock: setGState(ev, ev.G, gRunning, gWaiting) - ctx.threadStats.insyscall++ + if getGInfo(ev.G).isSystemG { + ctx.threadStats.insyscallRuntime++ + } else { + ctx.threadStats.insyscall++ + } case trace.EvGoSched, trace.EvGoPreempt: setGState(ev, ev.G, gRunning, gRunnable) case trace.EvGoStop, @@ -440,7 +453,11 @@ func generateTrace(params *traceParams) (ViewerData, error) { case trace.EvGoInSyscall: // Cancel out the effect of EvGoCreate at the beginning. setGState(ev, ev.G, gRunnable, gWaiting) - ctx.threadStats.insyscall++ + if getGInfo(ev.G).isSystemG { + ctx.threadStats.insyscallRuntime++ + } else { + ctx.threadStats.insyscall++ + } case trace.EvHeapAlloc: ctx.heapStats.heapAlloc = ev.Args[0] case trace.EvNextGC: @@ -449,8 +466,8 @@ func generateTrace(params *traceParams) (ViewerData, error) { if setGStateErr != nil { return ctx.data, setGStateErr } - if ctx.gstates[gRunnable] < 0 || ctx.gstates[gRunning] < 0 || ctx.threadStats.insyscall < 0 { - 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) + if ctx.gstates[gRunnable] < 0 || ctx.gstates[gRunning] < 0 || ctx.threadStats.insyscall < 0 || ctx.threadStats.insyscallRuntime < 0 { + 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) } // Ignore events that are from uninteresting goroutines @@ -663,7 +680,9 @@ func (ctx *traceContext) emitThreadCounters(ev *trace.Event) { if ctx.prevThreadStats == ctx.threadStats { return } - ctx.emit(&ViewerEvent{Name: "Threads", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &threadCountersArg{ctx.threadStats.prunning, ctx.threadStats.insyscall}}) + ctx.emit(&ViewerEvent{Name: "Threads", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &threadCountersArg{ + Running: ctx.threadStats.prunning, + InSyscall: ctx.threadStats.insyscall}}) ctx.prevThreadStats = ctx.threadStats }