1// run -gcflags -l=4 2 3// Copyright 2017 The Go Authors. All rights reserved. 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file. 6 7package main 8 9import ( 10 "fmt" 11 "runtime" 12) 13 14type frame struct { 15 pc uintptr 16 file string 17 line int 18 ok bool 19} 20 21var ( 22 skip int 23 globalFrame frame 24) 25 26func f() { 27 g() // line 27 28} 29 30func g() { 31 h() // line 31 32} 33 34func h() { 35 x := &globalFrame 36 x.pc, x.file, x.line, x.ok = runtime.Caller(skip) // line 36 37} 38 39//go:noinline 40func testCaller(skp int) frame { 41 skip = skp 42 f() // line 42 43 frame := globalFrame 44 if !frame.ok { 45 panic(fmt.Sprintf("skip=%d runtime.Caller failed", skp)) 46 } 47 return frame 48} 49 50type wantFrame struct { 51 funcName string 52 line int 53} 54 55// -1 means don't care 56var expected = []wantFrame{ 57 0: {"main.h", 36}, 58 1: {"main.g", 31}, 59 2: {"main.f", 27}, 60 3: {"main.testCaller", 42}, 61 4: {"main.main", 68}, 62 5: {"runtime.main", -1}, 63 6: {"runtime.goexit", -1}, 64} 65 66func main() { 67 for i := 0; i <= 6; i++ { 68 frame := testCaller(i) // line 68 69 fn := runtime.FuncForPC(frame.pc) 70 if expected[i].line >= 0 && frame.line != expected[i].line { 71 panic(fmt.Sprintf("skip=%d expected line %d, got line %d", i, expected[i].line, frame.line)) 72 } 73 if fn.Name() != expected[i].funcName { 74 panic(fmt.Sprintf("skip=%d expected function %s, got %s", i, expected[i].funcName, fn.Name())) 75 } 76 } 77} 78