一個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.pswp.cn/news/252699.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/252699.shtml
英文地址,請注明出處:http://en.pswp.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…