diff --git a/src/runtime/race.go b/src/runtime/race.go index c03866fd948..e2767f0324e 100644 --- a/src/runtime/race.go +++ b/src/runtime/race.go @@ -175,8 +175,11 @@ func raceSymbolizeCode(ctx *symbolizeCodeContext) { u, uf := newInlineUnwinder(fi, pc, nil) for ; uf.valid(); uf = u.next(uf) { sf := u.srcFunc(uf) - if sf.funcID == abi.FuncIDWrapper { - // ignore wrappers + if sf.funcID == abi.FuncIDWrapper && u.isInlined(uf) { + // Ignore wrappers, unless we're at the outermost frame of u. + // A non-inlined wrapper frame always means we have a physical + // frame consisting entirely of wrappers, in which case we'll + // take a outermost wrapper over nothing. continue } diff --git a/src/runtime/race/output_test.go b/src/runtime/race/output_test.go index 0dcdabe6411..4c2c3397cf8 100644 --- a/src/runtime/race/output_test.go +++ b/src/runtime/race/output_test.go @@ -439,4 +439,42 @@ Goroutine [0-9] \(running\) created at: main\.main\(\) .*/main.go:[0-9]+ \+0x[0-9,a-f]+ ==================`}}, + // Test symbolizing wrappers. Both (*T).f and main.func1 are wrappers. + // go.dev/issue/60245 + {"wrappersym", "run", "", "atexit_sleep_ms=0", ` +package main +import "sync" +var wg sync.WaitGroup +var x int +func main() { + f := (*T).f + wg.Add(2) + go f(new(T)) + f(new(T)) + wg.Wait() +} +type T struct{} +func (t T) f() { + x = 42 + wg.Done() +} +`, []string{`================== +WARNING: DATA RACE +Write at 0x[0-9,a-f]+ by goroutine [0-9]: + main\.T\.f\(\) + .*/main.go:15 \+0x[0-9,a-f]+ + main\.\(\*T\)\.f\(\) + :1 \+0x[0-9,a-f]+ + main\.main\.func1\(\) + .*/main.go:9 \+0x[0-9,a-f]+ + +Previous write at 0x[0-9,a-f]+ by main goroutine: + main\.T\.f\(\) + .*/main.go:15 \+0x[0-9,a-f]+ + main\.\(\*T\)\.f\(\) + :1 \+0x[0-9,a-f]+ + main\.main\(\) + .*/main.go:10 \+0x[0-9,a-f]+ + +`}}, }