1// Copyright 2023 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package runtime 6 7import ( 8 "internal/abi" 9 "internal/stringslite" 10 "runtime/internal/sys" 11) 12 13func XTestInlineUnwinder(t TestingT) { 14 if TestenvOptimizationOff() { 15 t.Skip("skipping test with inlining optimizations disabled") 16 } 17 18 pc1 := abi.FuncPCABIInternal(tiuTest) 19 f := findfunc(pc1) 20 if !f.valid() { 21 t.Fatalf("failed to resolve tiuTest at PC %#x", pc1) 22 } 23 24 want := map[string]int{ 25 "tiuInlined1:3 tiuTest:10": 0, 26 "tiuInlined1:3 tiuInlined2:6 tiuTest:11": 0, 27 "tiuInlined2:7 tiuTest:11": 0, 28 "tiuTest:12": 0, 29 } 30 wantStart := map[string]int{ 31 "tiuInlined1": 2, 32 "tiuInlined2": 5, 33 "tiuTest": 9, 34 } 35 36 // Iterate over the PCs in tiuTest and walk the inline stack for each. 37 prevStack := "x" 38 for pc := pc1; pc < pc1+1024 && findfunc(pc) == f; pc += sys.PCQuantum { 39 stack := "" 40 u, uf := newInlineUnwinder(f, pc) 41 if file, _ := u.fileLine(uf); file == "?" { 42 // We're probably in the trailing function padding, where findfunc 43 // still returns f but there's no symbolic information. Just keep 44 // going until we definitely hit the end. If we see a "?" in the 45 // middle of unwinding, that's a real problem. 46 // 47 // TODO: If we ever have function end information, use that to make 48 // this robust. 49 continue 50 } 51 for ; uf.valid(); uf = u.next(uf) { 52 file, line := u.fileLine(uf) 53 const wantFile = "symtabinl_test.go" 54 if !stringslite.HasSuffix(file, wantFile) { 55 t.Errorf("tiuTest+%#x: want file ...%s, got %s", pc-pc1, wantFile, file) 56 } 57 58 sf := u.srcFunc(uf) 59 60 name := sf.name() 61 const namePrefix = "runtime." 62 if stringslite.HasPrefix(name, namePrefix) { 63 name = name[len(namePrefix):] 64 } 65 if !stringslite.HasPrefix(name, "tiu") { 66 t.Errorf("tiuTest+%#x: unexpected function %s", pc-pc1, name) 67 } 68 69 start := int(sf.startLine) - tiuStart 70 if start != wantStart[name] { 71 t.Errorf("tiuTest+%#x: want startLine %d, got %d", pc-pc1, wantStart[name], start) 72 } 73 if sf.funcID != abi.FuncIDNormal { 74 t.Errorf("tiuTest+%#x: bad funcID %v", pc-pc1, sf.funcID) 75 } 76 77 if len(stack) > 0 { 78 stack += " " 79 } 80 stack += FmtSprintf("%s:%d", name, line-tiuStart) 81 } 82 83 if stack != prevStack { 84 prevStack = stack 85 86 t.Logf("tiuTest+%#x: %s", pc-pc1, stack) 87 88 if _, ok := want[stack]; ok { 89 want[stack]++ 90 } 91 } 92 } 93 94 // Check that we got all the stacks we wanted. 95 for stack, count := range want { 96 if count == 0 { 97 t.Errorf("missing stack %s", stack) 98 } 99 } 100} 101 102func lineNumber() int { 103 _, _, line, _ := Caller(1) 104 return line // return 0 for error 105} 106 107// Below here is the test data for XTestInlineUnwinder 108 109var tiuStart = lineNumber() // +0 110var tiu1, tiu2, tiu3 int // +1 111func tiuInlined1() { // +2 112 tiu1++ // +3 113} // +4 114func tiuInlined2() { // +5 115 tiuInlined1() // +6 116 tiu2++ // +7 117} // +8 118func tiuTest() { // +9 119 tiuInlined1() // +10 120 tiuInlined2() // +11 121 tiu3++ // +12 122} // +13 123