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

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

    背景是在写个日志库,日志库有个很重要的功能就是要打印出调用栈,知道具体是哪个文件,哪个函数调用的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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CMOS Sensor的调试经验分享

转自&#xff1a;http://bbs.52rd.com/forum.php?modviewthread&tid276351 CMOS Sensor的调试经验分享      我这里要介绍的就是CMOS摄像头的一些调试经验。   首先&#xff0c;要认识CMOS摄像头的结构。我们通常拿到的是集成封装好的模组&#xff0c;一般由三个部…

Learn Python—表达式、数据类型、流程控制

表达式 在 Python 中&#xff0c;2 2 称为“表达式”&#xff0c;它是语言中最基本的编程结构。表达式包含“值”&#xff08;例如2&#xff09;和“操作符”&#xff08;例如&#xff09;&#xff0c;并且总是可以求值&#xff08;也就是归约&#xff09;为单个值。这意味着在…

监控工具之zabbix server3.4 部署配置

[rootlocalhost src]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) [rootlocalhost src]# pwd /usr/local/src 配置zabbix的yum源 [rootlocalhost src]# rpm -ivh http://repo.zabbix.com/zabbix/3.4/rhel/7/x86_64/zabbix-release-3.4-2.el7.noarch.rpm …

CMOS Sensor基础知识

CMOS Sensor基础知识 曝光时间以行长为单位&#xff1b; PCLK以Hz为单位&#xff1b; 行长以周期数为单位&#xff0c;帧长以行长数为单位&#xff1b;其中周期数就是频率 T 周期以ms为单位&#xff1b; f 频率以Hz为单位&#xff1b; f 1 / T&#xff1b; Vsync Dummy Line…

java获取mp3的时长和播放mp3文件

所需包为jaudiotagger-2.2.6-SNAPSHOT.jar和jl1.0.1.jar。 import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream;import org.jaudiotagger.audio.AudioFileIO; import org.jaudiotagger.audio.mp3.MP3AudioHeader; import org.jaudiotag…

Redis 优缺点

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。 Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 Redis 与其他 key - value 缓存产品…

Python并发编程之concurrent.futures

2019独角兽企业重金招聘Python工程师标准>>> concurrent.futures模块提供了一个异步执行callables的高级接口。 可以使用ThreadPoolExecutor和ProcessPoolExecutor。 两者都继承了相同的接口&#xff0c;该接口由抽象的Executor类定义。 一个抽象类&#xff0c;提供…

1.3链表

链表的物理存储结构是用一组地址任意的存储单元存储数据的。不像顺序表占据连续的一段内存空间&#xff0c;而是将存储单元分散在内存的任意地址上。 链表结构中&#xff0c;每个数据元素记录都存放到链表的一个节点&#xff08;node&#xff09;中&#xff0c;而每个节点之间由…

移植opencv3.20到3556AV100

1.移植环境&#xff1a; Ubuntu14.04 arm-hisiv200-linux-opencv3.20 下载地址 2.移植步骤&#xff1a; 1&#xff09;安装cmake-gui 2&#xff09;新建一个opencv目录存放opencv-3.2.0.zip&#xff0c;并解压 击Browse Source选择~/hisi/opencv/opencv-3.2.0 点击Brow…

ngnix 详解

4 Nginx的rpm软件包安装 4.1 安装包在位置 D:\讲课内容--\新巴巴运动网\nginx高并发解决\nginx安装包 4.2 此种安装方式不用安装gcc等编译工具 4.3 安装命令如下 rpm –ivh nginx 5 配置虚拟主机 5.1 什么是虚拟主机 虚拟主机是一种特殊的软硬件技术&#xff0c;它可以将网络上…

iscroll5制作上下拉刷新 tab出现的问题

1.iscoll5插件刷新后如果想改变现实位置如果向下几px可以用 myScroll.scrollBy(0,0);方法&#xff0c;该值是相对当前位置。 2.iscoll5用到tab的时候&#xff0c;用点击生成iscoll对象出现取消不了之前的对象的绑定事件&#xff0c;点击多次后刷新执行多次的问题&#xff0c;解…

初谈逻辑读、物理读、预读

前言&#xff1a; 该文并不全是本人原创&#xff0c;里面的某些原理来自于CareySon。 SQL SERVER数据存储的形式 要理解逻辑读、物理读、预读这三个概念&#xff0c;先要搞懂SQL Server的数据存储方式。 SQL Server数据库包括数据文件和日志文件&#xff0c;一个数据库可以有一…

Makefile常用万能模板(包括静态链接库、动态链接库、可执行文件)

1、生成可执行文件的makefile2、生成静态链接库的makefile3、生成动态链接库的makefile 本文把makefile 分成了三份&#xff1a;生成可执行文件的makefile&#xff0c;生成静态链接库的makefile&#xff0c;生成动态链接库的makefile。 这些makefile都很简单&#xff0c;一般都…

TSQLDBServerHttpApi使用工作线程池

TSQLDBServerHttpApi使用工作线程池 TSQLDBServerHttpApi创建时&#xff0c;默认是使用单线程模式&#xff0c;且只使用一个数据库连接&#xff0c;服务端要应对众多的客户端只靠一个工作线程&#xff08;主线程&#xff09;和一个数据库连接&#xff0c; 服务端主线程不忙死才…

hibernate

Hibernate是一个开放源代码的对象关系映射框架&#xff0c;他对JDBC进行了轻量级的封装&#xff0c;使Java开发员可以随心所欲的使用对象编程思维操作数据库。 SessionFactory接口负责初始化Hibernate.他充当数据储存源的代理&#xff0c;并负责创建Session对象。 Session&…

Python数据分析之pandas入门

一、pandas库简介 pandas是一个专门用于数据分析的开源Python库&#xff0c;目前很多使用Python分析数据的专业人员都将pandas作为基础工具来使用。pandas是以Numpy作为基础来设计开发的&#xff0c;Numpy是大量Python数据科学计算库的基础&#xff0c;pandas以此为基础&#x…

激光雷达和毫米波雷达的区别

什么是激光雷达 激光雷达&#xff0c;是以发射激光束探测目标的位置、速度等特征量的雷达系统。其工作原理是向目标发射探测信号&#xff08;激光束&#xff09;&#xff0c;然后将接收到的从目标反射回来的信号&#xff08;目标回波&#xff09;与发射信号进行比较&#xff0c…

Git—使用方法

1、:插件的安装&#xff08;eclipse LUNA版本之后已经自动集成&#xff0c;不需要安装插件&#xff09;、 * 先打开该网页提供了对应版本的EGit&#xff0c;自己选择相应的版本。&#xff08;http://wiki.eclipse.org/EGit/FAQ#Where_can_I_find_older_releases_of_EGit.3F&…

激光雷达与毫米波雷达对比

激光雷达是一种采用非接触激光测距技术的扫描式传感器&#xff0c;其工作原理与一般的雷达系统类似&#xff0c;通过发射激光光束来探测目标&#xff0c;并通过搜集反射回来的光束来形成点云和获取数据&#xff0c;这些数据经光电处理后可生成为精确的三维立体图像。采用这项技…

安全可靠国产系统下的应用怎么搭建?

据国家信息安全漏洞共享平台&#xff08;CNVD&#xff09;统计数据&#xff0c;2016年我国共收录通用软硬件漏洞 10822个&#xff0c;漏洞来源涵盖了众多知名的国外厂商。应用软件的不安全性对我国信息技术发展产生了重大威胁&#xff0c;近年来我国频繁发布信息安全相关政策&a…