github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/start_line_test.go (about) 1 // Copyright 2022 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 5 package runtime_test 6 7 import ( 8 "fmt" 9 "internal/testenv" 10 "runtime" 11 "testing" 12 ) 13 14 // The tests in this file test the function start line metadata included in 15 // _func and inlinedCall. TestStartLine hard-codes the start lines of functions 16 // in this file. If code moves, the test will need to be updated. 17 // 18 // The "start line" of a function should be the line containing the func 19 // keyword. 20 21 func normalFunc() int { 22 return callerStartLine(false) 23 } 24 25 func multilineDeclarationFunc() int { 26 return multilineDeclarationFunc1(0, 0, 0) 27 } 28 29 //go:noinline 30 func multilineDeclarationFunc1( 31 a, b, c int) int { 32 return callerStartLine(false) 33 } 34 35 func blankLinesFunc() int { 36 37 // Some 38 // lines 39 // without 40 // code 41 42 return callerStartLine(false) 43 } 44 45 func inlineFunc() int { 46 return inlineFunc1() 47 } 48 49 func inlineFunc1() int { 50 return callerStartLine(true) 51 } 52 53 var closureFn func() int 54 55 func normalClosure() int { 56 // Assign to global to ensure this isn't inlined. 57 closureFn = func() int { 58 return callerStartLine(false) 59 } 60 return closureFn() 61 } 62 63 func inlineClosure() int { 64 return func() int { 65 return callerStartLine(true) 66 }() 67 } 68 69 func TestStartLine(t *testing.T) { 70 // We test inlined vs non-inlined variants. We can't do that if 71 // optimizations are disabled. 72 testenv.SkipIfOptimizationOff(t) 73 74 testCases := []struct { 75 name string 76 fn func() int 77 want int 78 }{ 79 { 80 name: "normal", 81 fn: normalFunc, 82 want: 21, 83 }, 84 { 85 name: "multiline-declaration", 86 fn: multilineDeclarationFunc, 87 want: 30, 88 }, 89 { 90 name: "blank-lines", 91 fn: blankLinesFunc, 92 want: 35, 93 }, 94 { 95 name: "inline", 96 fn: inlineFunc, 97 want: 49, 98 }, 99 { 100 name: "normal-closure", 101 fn: normalClosure, 102 want: 57, 103 }, 104 { 105 name: "inline-closure", 106 fn: inlineClosure, 107 want: 64, 108 }, 109 } 110 111 for _, tc := range testCases { 112 t.Run(tc.name, func(t *testing.T) { 113 got := tc.fn() 114 if got != tc.want { 115 t.Errorf("start line got %d want %d", got, tc.want) 116 } 117 }) 118 } 119 } 120 121 //go:noinline 122 func callerStartLine(wantInlined bool) int { 123 var pcs [1]uintptr 124 n := runtime.Callers(2, pcs[:]) 125 if n != 1 { 126 panic(fmt.Sprintf("no caller of callerStartLine? n = %d", n)) 127 } 128 129 frames := runtime.CallersFrames(pcs[:]) 130 frame, _ := frames.Next() 131 132 inlined := frame.Func == nil // Func always set to nil for inlined frames 133 if wantInlined != inlined { 134 panic(fmt.Sprintf("caller %s inlined got %v want %v", frame.Function, inlined, wantInlined)) 135 } 136 137 return runtime.FrameStartLine(&frame) 138 }