Skip to content

bug: go unwinding stops at systemstack #1275

@felixge

Description

@felixge

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:

Image

Actual Behavior

The eBPF profiler seems to stop unwinding at the systemstack, so devfiler produces the following flame graph:

Image

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions