From 742fda95246958076e439bbcf71fedda43a894bb Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Fri, 27 Jun 2025 00:59:49 +0000 Subject: runtime: account for missing frame pointer in preamble If a goroutine is synchronously preempted, then taking a frame-pointer-based stack trace at that preemption will skip PC of the caller of the function which called into morestack. This happens because the frame pointer is pushed to the stack after the preamble, leaving the stack in an odd state for frame pointer unwinding. Deal with this by marking a goroutine as synchronously preempted and using that signal to load the missing PC from the stack. On LR platforms this is available in gp.sched.lr. On non-LR platforms like x86, it's at gp.sched.sp, because there are no args, no locals, and no frame pointer pushed to the SP yet. For #68090. Change-Id: I73a1206d8b84eecb8a96dbe727195da30088f288 Reviewed-on: https://go-review.googlesource.com/c/go/+/684435 Reviewed-by: Cherry Mui LUCI-TryBot-Result: Go LUCI Reviewed-by: Nick Ripley --- src/internal/trace/trace_test.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src/internal/trace/trace_test.go') diff --git a/src/internal/trace/trace_test.go b/src/internal/trace/trace_test.go index eaf194cf07..44b7055344 100644 --- a/src/internal/trace/trace_test.go +++ b/src/internal/trace/trace_test.go @@ -326,7 +326,8 @@ func TestTraceStacks(t *testing.T) { const mainLine = 21 want := []evDesc{ {trace.EventStateTransition, "Goroutine Running->Runnable", []frame{ - {"main.main", mainLine + 82}, + {"runtime.Gosched", 0}, + {"main.main", mainLine + 87}, }}, {trace.EventStateTransition, "Goroutine NotExist->Runnable", []frame{ {"main.main", mainLine + 11}, @@ -349,7 +350,7 @@ func TestTraceStacks(t *testing.T) { }}, {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ {"runtime.chansend1", 0}, - {"main.main", mainLine + 84}, + {"main.main", mainLine + 89}, }}, {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ {"runtime.chansend1", 0}, @@ -357,7 +358,7 @@ func TestTraceStacks(t *testing.T) { }}, {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ {"runtime.chanrecv1", 0}, - {"main.main", mainLine + 85}, + {"main.main", mainLine + 90}, }}, {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ {"runtime.selectgo", 0}, @@ -365,7 +366,7 @@ func TestTraceStacks(t *testing.T) { }}, {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ {"runtime.selectgo", 0}, - {"main.main", mainLine + 86}, + {"main.main", mainLine + 91}, }}, {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ {"sync.(*Mutex).Lock", 0}, @@ -382,7 +383,7 @@ func TestTraceStacks(t *testing.T) { {trace.EventStateTransition, "Goroutine Waiting->Runnable", []frame{ {"sync.(*WaitGroup).Add", 0}, {"sync.(*WaitGroup).Done", 0}, - {"main.main", mainLine + 91}, + {"main.main", mainLine + 96}, }}, {trace.EventStateTransition, "Goroutine Running->Waiting", []frame{ {"sync.(*Cond).Wait", 0}, @@ -402,6 +403,10 @@ func TestTraceStacks(t *testing.T) { {"runtime.GOMAXPROCS", 0}, {"main.main", 0}, }}, + {trace.EventStateTransition, "Goroutine Running->Runnable", []frame{ + {"main.syncPreemptPoint", 0}, + {"main.main.func12", 0}, + }}, } if !stress { // Only check for this stack if !stress because traceAdvance alone could -- cgit v1.3