一个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,一经查实,立即删除!

相关文章

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

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

Python并发编程之concurrent.futures

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

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

前言&#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;一般都…

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…

Win10 + Python + MXNet + VS2015配置

项目需要使用MTCNN来检测、对齐、剪切出人脸&#xff0c;它是使用MXNet作为框架的&#xff0c;但是我自己的Ubuntu里各种框架乱成一团&#xff0c;不想再添乱就铁了心要在windows里配一个。无奈网上的资料不多&#xff0c;挣扎了几天之后决定留下这么一份文档。 首先我们使用的…

bzoj 3224 Tyvj 1728 普通平衡树

题目大意&#xff1a; 您需要写一种数据结构&#xff08;可参考题目标题&#xff09;&#xff0c;来维护一些数&#xff0c;其中需要提供以下操作&#xff1a; 1. 插入x数 2. 删除x数(若有多个相同的数&#xff0c;因只删除一个) 3. 查询x数的排名(若有多个相同的数&#xff0c…

不懂毫米波雷达?5分钟读懂毫米波雷达的那些事儿

2019年是毫米波风生水起的一年&#xff0c;也是毫米波名声大噪的一年。毫米波应用范围广泛&#xff0c;如毫米波雷达、毫米波天线等。而本文&#xff0c;将向大家介绍毫米波雷达&#xff0c;主要内容包括&#xff1a;毫米波雷达原理、毫米波雷达主要特点、毫米波雷达优势以及毫…

redis集群的搭建

########环境######### centos 7.2 , gcch 环境ruby 2.0.0 redis 3.2.8 redis-3.3.3gem 公司要求搭建redis集群, 本来觉得挺好搞的,没想到弄到现在.... 1, 环境准备 gcc , ruby 等环境准备 yum -y install gcc ruby ruby-devel rubygems rpm-build zlib redis-ruby接口安装, 我…

2017-2018-1 20155227 《信息安全系统设计基础》第十三周学习总结

2017-2018-1 20155227 《信息安全系统设计基础》第十三周学习总结 找出全书你认为最重要的一章&#xff0c;深入重新学习一下&#xff0c;要求&#xff08;期末占10分&#xff09;&#xff1a; 完成这一章所有习题详细总结本章要点给你的结对学习搭档讲解你的总结并获取反馈我选…

进程间五种通信方式

进程间通信&#xff08;IPC&#xff0c;InterProcess Communication&#xff09;是指在不同进程之间传播或交换信息。 IPC的方式通常有管道&#xff08;包括无名管道和命名管道&#xff09;、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机…

电子书下载:Silverlight 5 in Action

下载&#xff1a;http://www.ctdisk.com/file/8447319

Virtualbox桥接网卡设置

正常情况下&#xff0c;像设置virtualbox虚拟机的桥接网卡非常简单&#xff0c;只需要点配置&#xff0c;然后在配置界面点击网络&#xff0c;然后在右边的网络里选择桥接网络即可。但是如果这么简单就好了&#xff0c;今天要说的就是在不正常的情况下是怎么设置的。 工具/原料…

SQL疑难杂症【4 】大量数据查询的时候避免子查询

前几天发现系统变得很慢&#xff0c;在Profiler里面发现有的SQL执行了几十秒才返回结果&#xff0c;当时的SQL如下&#xff1a; 可以看得出来&#xff0c;在652行用了子查询&#xff0c;恰巧目标表(QS_WIP)中的记录数为100000000&#xff0c;通过如下SQL可以得到&#xff1a; S…