2019独角兽企业重金招聘Python工程师标准>>>

背景是在写个日志库,日志库有个很重要的功能就是要打印出调用栈,知道具体是哪个文件,哪个函数调用的Info 等。 然后在测试中发现了一种写法,我自己本机测试一直ok, 但是业务使用的时候调用栈始终不对,打的调用栈少了一层。莫名其妙的,后来对比发现,我们就是go version 不一样。

go version :

go version go1.9.2 darwin/amd64

go env:

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/didi/Desktop/didi"
GORACE=""
GOROOT="/usr/local/go1.9.2"
GOTOOLDIR="/usr/local/go1.9.2/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/2w/tt1p_4td3yq9xlbl7c2t4jn00000gn/T/go-build427754844=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

我的示例代码是这样的:

package mainimport ("fmt""runtime"
)var i []bytetype AAA struct {
}func (a *AAA) test1() *AAA {buf := make([]byte, 1<<20)runtime.Stack(buf, true)fmt.Printf("\n%s", buf)return a
}func (a *AAA) test2() *AAA {i = append(i, "test2"...)return a
}func test() {a := AAA{}a.test1().test2()
}func main() {test()
}

然后呢,我期望的结果:

goroutine 1 [running]:
main.(*AAA).test1(0xc420045f60, 0x1003a4c)/Users/didi/Desktop/didi/src/test/testCall/main.go:15 +0x87
main.test()/Users/didi/Desktop/didi/src/test/testCall/main.go:27 +0x2f
main.main()/Users/didi/Desktop/didi/src/test/testCall/main.go:31 +0x20

但是真实结果是这样的:

goroutine 1 [running]:
main.(*AAA).test1(0xc42003df48, 0xc42003df70)/Users/didi/Desktop/didi/src/test/testCall/main.go:15 +0x87
main.(*AAA).test2(...)/Users/didi/Desktop/didi/src/test/testCall/main.go:27
main.test()/Users/didi/Desktop/didi/src/test/testCall/main.go:27 +0x2f
main.main()/Users/didi/Desktop/didi/src/test/testCall/main.go:31 +0x20

问题来了,我日志库封装要是有这种类似逻辑,那打印的日志全都是有问题的,怎么可能是test2调用test1? 莫名其妙的。。。

初步怀疑是内联引起的问题,这里现象看着很像。编译,加上不允许内联后,问题解决,  解决方式蛮简单的,函数前加个 // go:noinline。

为什么会出现这种让人困惑的现象,通过查看go 官方issue 和 release note  发现下面解释:

Users of runtime.Callers should avoid directly inspecting the resulting PC slice and instead use runtime.CallersFrames to get a complete view of the call stack, or runtime.Caller to get information about a single caller. This is because an individual element of the PC slice cannot account for inlined frames or other nuances of the call stack.
// 使用runtime.Caller 不能显示内联的细微区别。Specifically, code that directly iterates over the PC slice and uses functions such as runtime.FuncForPC to resolve each PC individually will miss inlined frames. To get a complete view of the stack, such code should instead use CallersFrames. Likewise, code should not assume that the length returned by Callers is any indication of the call depth. It should instead count the number of frames returned by CallersFrames.Code that queries a single caller at a specific depth should use Caller rather than passing a slice of length 1 to Callers.runtime.CallersFrames has been available since Go 1.7, so code can be updated prior to upgrading to Go 1.9.

然后官方有人提了这个issue, https://github.com/golang/go/issues/22916。总结就是,官方在1.9 的时候觉得1.8及以前版本的不对,Caller 应该将内联栈也算进去。然后后来大家觉得这种使用不符合习惯,在1.10 又改回去了。我个人试了下,1.10.x, 1.11.x 都是正常的。

这种问题,大多数人应该遇不上,一个是要求链式调用的写法,第二个得关心调用栈,才会遇到这种奇怪现象。

转载于:https://my.oschina.net/u/2950272/blog/2995702

一个go1.9.x 编译器内联引起的栈信息错乱的问题分析相关推荐

  1. 使用内联函数的一个问题

    2019独角兽企业重金招聘Python工程师标准>>> 最近碰到一个与内联方法有关的编译问题,记叙如下. 问题背景 类Scheduler的实现如下所示,其中方法SetStates() ...

  2. C++中的内联函数inline

    1.Cpp中的内联函数 内联函数是通常与类一起使用.如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方.对内联函数进行任何修改,都需要重新编译函数的所有客户端,因 ...

  3. c++学习笔记内联函数,函数重载,默认参数

    c++学习笔记内联函数,函数重载,默认参数 1 inline内联函数 C++中的const常量可以替代宏常数定义,如: const int A = 3;  #define A 3 C++中是否有解决 ...

  4. go 基准测试 找不到函数_Go 中的内联优化 | Linux 中国

    本文讨论 Go 编译器是如何实现内联的,以及这种优化方法如何影响你的 Go 代码.https://linux.cn/article-12176-1.html作者:Dave Cheney译者:Xiaob ...

  5. C6000系列DSP的内联函数

    在c中,为了解决一些频繁调用的小函数大量消耗栈空间或是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数数.栈空间就是指放置程序的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限 ...

  6. C++对象模型9——临时对象的生命周期、模板及实例化分析、内联函数

    一.临时对象的生命周期 T c=a+b 假设T是一个类型,那么上述代码执行时,首先会产生一个临时对象用来存放a+b的结果(拷贝初始化临时对象),然后用该临时对象拷贝初始化c,最后临时对象被释放.如果开 ...

  7. c++中内敛函数_C++ 内联函数 | 菜鸟教程

    内联函数: Tip: 只有当函数只有 10 行甚至更少时才将其定义为内联函数. 定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用. 优点: 当函数体比较 ...

  8. C++内联函数学习总结

    C++中的内联函数inline总结 http://blog.csdn.net/coder_xia/article/details/6723387 突然看到C++Primer中讲到,对于vector的一 ...

  9. C++内联函数(inline function)

    c++从c中继承的一个重要特征就是效率.假如c++的效率明显低于c的效率,那么就会有很大的一批程序员不去使用c++了. 在c中我们经常把一些短并且执行频繁的计算写成宏,而不是函数,这样做的理由是为了执 ...

最新文章

  1. 开启报名丨中文信息学会青工委学术沙龙:“推荐系统前沿进展”
  2. jmeter固定定时器使用与思考
  3. SAP ABAP逻辑数据库
  4. pstack 安装linux_详解命令-pstack
  5. Failed building wheel for scandir 解决方案
  6. matlab help函数用法,MATLAB函数用法
  7. 基于QT开发的线性代数初学者的矩阵计算器设计
  8. U盘仅显示几兆的解决方法
  9. 仅用 1/4 数据量还原真人语音100%细节,火山语音上新超自然对话语音合成技术...
  10. 基于微信小程序的评分小程序
  11. Jquery实现点击事件的四种写法
  12. R语言计算夹角余弦(Consine)
  13. 华尔街人必读40本金融佳作
  14. EMC传导干扰滤波电路的设计
  15. The road to learning English-Writing
  16. python处理excel的书籍_从Excel到Python:用Python轻松处理Excel数据
  17. 关于速营社,我也是偶遇
  18. PTA 6-2 计算长方体的表面积和体积
  19. FPGA同步和异步电路
  20. PCIe体系结构的组成部件

热门文章

  1. 蓝字冲销是什么意思_梦见上学 做梦梦到上学是什么意思 梦到上学有哪些预兆...
  2. UFLDL教程:数据预处理
  3. P多行溢出省略号的处理
  4. SpringBoot(十三)-- 不同环境下读取不同配置
  5. hbase replication原理分析
  6. 基于visual Studio2013解决面试题之0807strstr函数
  7. 订单不断,我是这样做的
  8. 在.Net如何制作自定义的快捷方式(转)
  9. 面向对象与基于对象 区别
  10. react-router-dom v6.1.1 使用方式