blob: 6c4faa87a8cbf923e4a39ac3c377d1e737675786 [file] [log] [blame] [edit]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime_test
import (
"fmt"
"internal/testenv"
"runtime"
"testing"
)
// The tests in this file test the function start line metadata included in
// _func and inlinedCall. TestStartLine hard-codes the start lines of functions
// in this file. If code moves, the test will need to be updated.
//
// The "start line" of a function should be the line containing the func
// keyword.
func normalFunc() int {
return callerStartLine(false)
}
func multilineDeclarationFunc() int {
return multilineDeclarationFunc1(0, 0, 0)
}
//go:noinline
func multilineDeclarationFunc1(
a, b, c int) int {
return callerStartLine(false)
}
func blankLinesFunc() int {
// Some
// lines
// without
// code
return callerStartLine(false)
}
func inlineFunc() int {
return inlineFunc1()
}
func inlineFunc1() int {
return callerStartLine(true)
}
var closureFn func() int
func normalClosure() int {
// Assign to global to ensure this isn't inlined.
closureFn = func() int {
return callerStartLine(false)
}
return closureFn()
}
func inlineClosure() int {
return func() int {
return callerStartLine(true)
}()
}
func TestStartLine(t *testing.T) {
// We test inlined vs non-inlined variants. We can't do that if
// optimizations are disabled.
testenv.SkipIfOptimizationOff(t)
testCases := []struct{
name string
fn func() int
want int
}{
{
name: "normal",
fn: normalFunc,
want: 21,
},
{
name: "multiline-declaration",
fn: multilineDeclarationFunc,
want: 30,
},
{
name: "blank-lines",
fn: blankLinesFunc,
want: 35,
},
{
name: "inline",
fn: inlineFunc,
want: 49,
},
{
name: "normal-closure",
fn: normalClosure,
want: 57,
},
{
name: "inline-closure",
fn: inlineClosure,
want: 64,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := tc.fn()
if got != tc.want {
t.Errorf("start line got %d want %d", got, tc.want)
}
})
}
}
//go:noinline
func callerStartLine(wantInlined bool) int {
var pcs [1]uintptr
n := runtime.Callers(2, pcs[:])
if n != 1 {
panic(fmt.Sprintf("no caller of callerStartLine? n = %d", n))
}
frames := runtime.CallersFrames(pcs[:])
frame, _ := frames.Next()
inlined := frame.Func == nil // Func always set to nil for inlined frames
if wantInlined != inlined {
panic(fmt.Sprintf("caller %s inlined got %v want %v", frame.Function, inlined, wantInlined))
}
return runtime.FrameStartLine(&frame)
}