It seems like the Go unwinder stops when it hits the systemstack frame. This frame usually marks the boundary between the g0 stack associated with the M and the real goroutine stack that called into it.
Steps to reproduce
Run the program below. I used go1.26, but I suspect the problem impacts all recent Go versions.
package main
import (
"os"
"runtime"
"runtime/pprof"
"time"
)
func main() {
f, err := os.Create("cpu.pprof")
if err != nil {
panic(err)
}
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
pcs := make([]uintptr, 32)
deadline := time.After(30 * time.Second)
for {
select {
case <-deadline:
return
default:
runtime.Callers(0, pcs)
}
}
}
While running the program, I ran the profiler at version fbc3686a (2026-03-20) and the devfiler at version 35fe854b (2026-03-18).
Expected Behavior
The program is spending most of its time in runtime.Callers, so I would expect the flame graph to show main calling into runtime.Callers. Go's built-in CPU profiler works as expected:
Actual Behavior
The eBPF profiler seems to stop unwinding at the systemstack, so devfiler produces the following flame graph:
Additional Notes
I suspect the problem is caused by the need to walk a stack transition. In theory gopclntab should have all the required metadata for this, so hopefully this is fixable.
If time allows, I'll try to take a shot at this myself. But I figured I should document the problem to begin with.
It seems like the Go unwinder stops when it hits the systemstack frame. This frame usually marks the boundary between the g0 stack associated with the M and the real goroutine stack that called into it.
Steps to reproduce
Run the program below. I used go1.26, but I suspect the problem impacts all recent Go versions.
While running the program, I ran the profiler at version
fbc3686a(2026-03-20) and the devfiler at version35fe854b(2026-03-18).Expected Behavior
The program is spending most of its time in
runtime.Callers, so I would expect the flame graph to showmaincalling intoruntime.Callers. Go's built-in CPU profiler works as expected:Actual Behavior
The eBPF profiler seems to stop unwinding at the
systemstack, so devfiler produces the following flame graph:Additional Notes
I suspect the problem is caused by the need to walk a stack transition. In theory
gopclntabshould have all the required metadata for this, so hopefully this is fixable.If time allows, I'll try to take a shot at this myself. But I figured I should document the problem to begin with.