}
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 {
)
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.
}
ctx.gstates[newState]++
info.state = newState
}
+
for _, ev := range ctx.events {
// Handle state transitions before we filter out events.
switch ev.Type {
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
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,
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:
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
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
}