
不知道哪一次更新,也不知道因?yàn)楦铝耸裁矗琍C 上的 IGV 突然就不能用了。除了換一臺(tái) Mac 以外還能怎么辦。下文記錄了 debug 的過(guò)程以及一點(diǎn)思考。
前奏IGV 這個(gè)工具因?yàn)槭?Java 全平臺(tái)適配的,一般不太容易出現(xiàn) bug。在 Windows 上常見(jiàn)的問(wèn)題是由于 Java 32位和64位版本問(wèn)題造成的。在 64 位的電腦上安裝了 32 位的 Java(通常默認(rèn)就是32位)后,如果給 IGV 分配的內(nèi)存超過(guò)了 2G,就會(huì)報(bào)內(nèi)存錯(cuò)誤,直接體現(xiàn)為 igb.bat 點(diǎn)擊后無(wú)法打開(kāi)。最直接的方法就是把 Java 升級(jí)到64位。 然而,不知道是哪一次更新,不知道是因?yàn)楦铝耸裁?,我?bug 表現(xiàn)是 IGV 可以正常啟動(dòng)但啟動(dòng)后只要進(jìn)行任意的一次點(diǎn)擊就會(huì)閃退。最初我的猜測(cè)是因?yàn)槟骋粋€(gè)內(nèi)容的更新,導(dǎo)致了Java,Windows 和 IGV 三者不兼容。于是我分別更新了最新版的 IGV 和最新版的 Java,然后還升級(jí)過(guò)一次電腦系統(tǒng),問(wèn)題都沒(méi)有得到解決。當(dāng)時(shí)時(shí)間有限,debug 的最直接方式就是繞開(kāi) bug。 替代方案必須要用的軟件突然不能用了又急著用,立刻買(mǎi)個(gè) Mac 就可以避免 Windows 上的問(wèn)題,雖然錢(qián)不是問(wèn)題,但問(wèn)題是沒(méi)錢(qián),所以只能尋找其它替代方案。因?yàn)?Java 本身是全平臺(tái)通用的,所以在服務(wù)器上下載了 IGV linux 版本,然后通過(guò) Xming 在本地電腦上調(diào)用 Linux 開(kāi)啟的 IGV 圖形操作界面。 這個(gè)方法解決了燃眉之急,但是從服務(wù)器通過(guò) Xming 在本地進(jìn)行點(diǎn)選,很多操作會(huì)用明顯的卡頓和延遲而且分辨率很低。并非長(zhǎng)久之計(jì)。 dubug 過(guò)程首先需要看一下相關(guān) IGV 報(bào)錯(cuò)信息 A fatal error has been detected by the Java Runtime Environment:
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffe51899e54, pid=8364, tid=0x0000000000004590
JRE version: Java(TM) SE Runtime Environment (8.0_191-b12) (build 1.8.0_191-b12)
Java VM: Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode windows-amd64 compressed oops)
Problematic frame:
C [atig6pxx.dll+0x9e54]
Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
If you would like to submit a bug report, please visit:
http://bugreport.Java.com/bugreport/crash.jsp
The crash happened outside the Java Virtual Machine in native code.
See problematic frame for where to report the bug.
閱讀報(bào)錯(cuò)信息因?yàn)槲冶旧聿欢?Java,只能根據(jù)報(bào)錯(cuò)提取自己認(rèn)為關(guān)鍵的內(nèi)容進(jìn)行檢索。 從以往的經(jīng)驗(yàn)來(lái)看,首先要重點(diǎn)關(guān)注哪里失敗或者不能啟動(dòng)。在 log 文件 header 部分恰好出現(xiàn)了一句話(huà)「Failed to write core dump. Minidumps are not enabled by default on client versions of Windows」,這個(gè)信息看起來(lái)非常關(guān)鍵,很可能是因?yàn)?Windows 不可以寫(xiě)入 core dump 導(dǎo)致的問(wèn)題,那么自然應(yīng)該首先開(kāi)啟 core dump 試試。 在 SO 上查到了 core dump 的開(kāi)啟方式,對(duì)于Java 8 使用 -XX:+CreateMinidumpOnCrash 參數(shù)即可。修改 igv.bat 文件再命令行里添加該參數(shù)后重新運(yùn)行,問(wèn)題依舊存在,只不過(guò)這次除了會(huì)生成 log 文件以外,還會(huì)生成一個(gè)大小幾 G 的 core dump 文件??磥?lái)這個(gè)問(wèn)題并不是 IGV 閃退的原因。 在檢索的過(guò)程中,有中文帖記錄出現(xiàn)這樣的問(wèn)題是因?yàn)椤窲RE version 和 JDK 不一樣」。所以重新下載一個(gè)版本對(duì)應(yīng)的 JDK 或者 JRE 就可以解決。雖然我一開(kāi)始其實(shí)也分不清他來(lái)有什么區(qū)別,也不確定自己是不是同時(shí)裝了這兩個(gè)東西,但是抱著試試又不會(huì)掛的心態(tài),還是再一次卸載了電腦里的Java。 因?yàn)閾?dān)心是之前 Java 升級(jí)有問(wèn)題,我還專(zhuān)門(mén)下載了官方的Java 卸載工具,想把自己電腦里所有和 Java 相關(guān)的東西都卸載個(gè)干干凈凈,重新來(lái)過(guò)。 按照 IGV 網(wǎng)站上給的 Java 8 下載鏈接,我又一次重新安裝了 Java,運(yùn)行之后真的還沒(méi)有出現(xiàn)閃退的情況,因?yàn)楦揪痛虿婚_(kāi)了。其實(shí)這就是上文提到的因?yàn)?IGV 網(wǎng)站上的 Java 下載鏈接引導(dǎo)我下載了 32 位 Java 版本,但是 IGV.bat 中使用的內(nèi)存配置是4G,超過(guò)了限額地緣故。我又不得不再一次卸載 32 位重新安裝了 64 位Java,閃退問(wèn)題依舊存在。 不過(guò),至此我確認(rèn)了電腦不存在所謂的「JRE和JDK版本不一致的問(wèn)題」,版本信息如下: # JRE version: Java(TM) SE Runtime Environment (8.0_191-b12) (build 1.8.0_191-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode windows-amd64 compressed oops)
控制變量Debug 一個(gè)非常重要的經(jīng)驗(yàn)是需要 控制變量。如果有條件,不妨找一臺(tái)能夠正確運(yùn)行某個(gè)程序的機(jī)器,看看它和自己報(bào)錯(cuò)的機(jī)器有什么差別。 為此,我分別查看其他人幾臺(tái)可以正確運(yùn)行 IGV 的 PC,發(fā)現(xiàn)有人的IGV版本較低,有人的 Java 版本較低,但是大家都可以正常使用,于是我又分別測(cè)試了幾個(gè)低版本的 Java 或者 IGV,然并卵。考慮自己的筆記本系統(tǒng)和軟件版本與我的工作 PC 高度一致,又用最新版的 IGV 和 Java 8 在筆記本上進(jìn)行了測(cè)試,神奇的是在筆記本卻可以正常運(yùn)行。 至此,我基本懵逼。因?yàn)閃indows 系統(tǒng)和 IGV 版本以及 Java 版本完全一致的情況下,筆記本可以正常運(yùn)行IGV,但是 PC 卻不可以。這里面一定有某些不可告人的力量。 理解報(bào)錯(cuò)信息通過(guò)上面幾步折騰,問(wèn)題的關(guān)鍵應(yīng)該不在于解決 「Failed to write core dump」而是需要再向前回溯,這個(gè)時(shí)候就要理解 Java 生成的log文件究竟傳達(dá)了哪些信息。 通常只有嚴(yán)重的錯(cuò)誤引起Java進(jìn)程非正常退出,出現(xiàn)了Crash,才會(huì)產(chǎn)生一個(gè)文件名為err+pid number 的日志文件。 檢索后發(fā)現(xiàn)很多 Java 的報(bào)錯(cuò)都會(huì)出現(xiàn) EXCEPTION_ACCESS_VIOLATION(0xc0000005) 這一句話(huà),他意味著Java 應(yīng)用 Crash 時(shí)正在運(yùn)行 JVM 自己的代碼,而不是外部的 Java 代碼。在這個(gè)位置還可能出現(xiàn) SIGSEGV(0xb) 或者 EXCEPTION_STACK_OVERFLOW 這樣的內(nèi)容。 再往下看另一個(gè)重要信息點(diǎn)是 Problematic frame:
C [atig6pxx.dll+0x9e54]
這里的信息是顯示 Crash 時(shí) JVM 正在從哪個(gè)庫(kù)文件執(zhí)行代碼。我遇到問(wèn)題是 C,還可能是 V 和 J 等等。他們的意思如下 FrameType Description:
C: Native C frame
j: Interpreted Java frame
V: VMframe
v: VMgenerated stub frame
J: Other frame types, including compiled Java frames
接下來(lái)我又閱讀了一下 Java 的 debug 指南,里面的分析思路都是要看 C 后面提示了什么信息,然后給了一些 debug 的建議: The first step to solving a crash in a native library is to investigate the source of the native library where the crash occurred. If the native library is provided by your application, then investigate the source code of your native library. A significant number of issues with JNI code can be identified by running the application with the -Xcheck:jni option added to the command line. See The -Xcheck:jni Option. If the native library has been provided by another vendor and is used by your application, then file a bug report against this third-party application and provide the fatal error log information. If the native library where the crash occurred is part of the Java Runtime Environment (JRE) (for example awt.dll, net.dll, and so forth), then it is possible that you have encountered a library or API bug. If so, gather as much data as possible and submit a bug or report, indicating the library name. You can find JRE libraries in the jre/lib or jre/bin directories of the JRE distribution. See Submit a Bug Report.
仔細(xì)閱讀之后發(fā)現(xiàn)基本沒(méi)讀出啥東西,但是現(xiàn)在從報(bào)錯(cuò)信息中我已經(jīng)明確了問(wèn)題應(yīng)該指向 atig6pxx.dll 。 指果所因既然定位到了atig6pxx.dll ,就要查查它是什么,搜索 atig6pxx.dll windows10 在前面幾條結(jié)果中,我看到了這樣幾個(gè)信息 AMD Graphics Driver not working/incompatible with Win 10 Technical Preview OpenGl for crimson drivers

如此看來(lái),這個(gè) atig6pxx.dll 和AMD 的顯卡驅(qū)動(dòng)有關(guān),而我的兩個(gè)顯示器之一顯卡確實(shí)是AMD的,從查詢(xún)結(jié)果來(lái)看,AMD 顯卡驅(qū)動(dòng)的這個(gè) atig6pxx.dll 和某些 win10 版本搭配在一起就存在問(wèn)題(我用的就是有問(wèn)題的版本?)。 接下來(lái)再試試 atig6pxx.dllJava 的搜索結(jié)果,真還搜到了相關(guān)的一個(gè)帖子,官方給的回復(fù)大意是要發(fā)帖子的人詳細(xì)確定顯卡和升級(jí)之類(lèi)的各種信息,看來(lái)這個(gè)問(wèn)題確實(shí)是 Java 和 PC 的顯卡驅(qū)動(dòng)有 bug。同時(shí)又聯(lián)想到我的控制變量實(shí)驗(yàn),筆記本是NVIDIA顯卡而不是AMD,進(jìn)一步相信問(wèn)題可能真出自 AMD 顯卡上。 解決問(wèn)題確定了bug的原因,接下來(lái)就要考慮如何解決問(wèn)題。 首先需要知道 Java 和顯卡驅(qū)動(dòng)是怎么能聯(lián)系到一起的,檢索一下 Javagraphics 找到了 官網(wǎng)的一些說(shuō)明。 這里主要涉及 Java 2D 這么一個(gè)東西,在搜索的過(guò)程中又找到了 Java 2D 的一些選項(xiàng)。在檢索的過(guò)程中,還從 SO 看到了一個(gè)稍微相關(guān)的帖子,其中一個(gè)建議也是對(duì)顯卡進(jìn)行一系列的測(cè)試,其中提到了參數(shù) -Dsun.Java2d.d3d=false 。這里的參數(shù)和Java 2D 的參數(shù)吻合而且有一點(diǎn)眼熟,看到這里就要回過(guò)頭重新看看 igv.bat 文件。 官方提供的 igv.bat 實(shí)際命令如下 ::Get the current batch file's short path
for %%x in (%0) do set BatchPath=%%~dpsx
for %%x in (%BatchPath%) do set BatchPath=%%~dpsx
Java -Xmx4g -Dproduction=true -DJava.net.preferIPv4Stack=true -Dsun.Java2d.noddraw=true -jar %BatchPath%\lib\IGV.jar %*
仔細(xì)看一下,官方這條命令實(shí)際調(diào)用的程序來(lái)自 lib 目錄的 igv.jar,只不過(guò)添加了幾個(gè)平時(shí)自己根本不會(huì)在意的參數(shù),其中 -Dsun.Java2d.noddraw=true 和之前 SO 中建議添加的參數(shù)非常類(lèi)似,而這個(gè)參數(shù)又是一個(gè)和顯示性能相關(guān)的參數(shù)。隱約感覺(jué)問(wèn)題就出在這里。 繼續(xù)貫徹控制變量的思想,首先不加任何參數(shù)在 lib 目錄下直接運(yùn)行 igv.jar,嗯,真的沒(méi)有閃退?;氐?bat 依次修改上面三個(gè)參數(shù)進(jìn)行測(cè)試,直到我把 -Dsun.Java2d.noddraw=true 改為 false 后,bug 消失,至此問(wèn)題解決。 復(fù)盤(pán)與反思綜上,通過(guò)學(xué)習(xí) log 文件,查找關(guān)鍵內(nèi)容,控制變量測(cè)試,一步一步把 Java 的運(yùn)行錯(cuò)誤和顯卡聯(lián)系起來(lái)。不過(guò)我是萬(wàn)萬(wàn)沒(méi)想到這錯(cuò)誤竟然是和顯卡相關(guān)。 進(jìn)一步了解下Dsun.Java2d.noddraw這個(gè)參數(shù)的作用 The following list describes some useful properties on Windows platforms. The DirectDraw/GDI pipeline is the default pipeline for Windows. Change this default as follows: -Dsun.Java2d.noddraw=true Disable the use of DirectDraw pipeline. GDI will be used instead.
-Dsun.Java2d.noddraw=false Enable the use of DirectDraw pipeline.
-Dsun.Java2d.d3d=false Disable the use of Direct3D pipeline.
也就是說(shuō),因?yàn)锳MD 顯卡驅(qū)動(dòng)中 atig6pxx.dll 的問(wèn)題,我目前的電腦并不能只使用 GDI 來(lái)進(jìn)行渲染,而是必須要開(kāi)啟 DirectDraw。至于更深次的原因暫時(shí)沒(méi)有深究的必要。 回顧整個(gè) debug 的過(guò)程,雖然一直在嘗試使用控制變量的思路,但是具體的測(cè)試順序不對(duì)。 使用別人的電腦來(lái)控制變量,不相關(guān)的因素太多,各種軟件版本的不一致只是表象,并沒(méi)有指明到是真正的顯卡差別;而使用自己的筆記本和臺(tái)式機(jī)做比較,才排除了軟件版本的關(guān)系。 最后進(jìn)行的控測(cè)試才是變量最可控的,應(yīng)該首先就進(jìn)行的在同一個(gè)電腦上對(duì)同樣的軟件進(jìn)行不同的操作。其中一個(gè)操作是官方并不推薦的直接運(yùn)行 lib 中的 igv.jar 程序,另一個(gè)操作是運(yùn)行官方推薦的添加了參數(shù)的 igv.bat 。如果把這個(gè)測(cè)試提到第一步來(lái)做,就會(huì)節(jié)省大量的測(cè)試時(shí)間。 最后,對(duì)于 debug 的思考:
仔細(xì)閱讀報(bào)錯(cuò)信息確定關(guān)鍵詞 成熟的工具一定有著非常詳細(xì)的報(bào)錯(cuò)信息,而我們要做的是從詳細(xì)的報(bào)錯(cuò)信息中提煉關(guān)鍵詞進(jìn)行檢索
按照從小到大的順序盡量找到可以正確運(yùn)行的方式 想要找到問(wèn)題所在很關(guān)鍵的一點(diǎn)知道誰(shuí)或者在哪里可以成功。這一步對(duì)之后的 debug 過(guò)程十分重要。 所謂從小到大的意思是正確運(yùn)行和錯(cuò)誤運(yùn)行之間的差異從小到大。最好可以在一臺(tái)電腦上完成,只是改變運(yùn)行方式或者參數(shù);如果不行就找配置盡量類(lèi)似的電腦或服務(wù)器進(jìn)行測(cè)試,以此類(lèi)推。如果我首先能想到拋棄官方的建議打開(kāi)方式去運(yùn)行 jar 程序,直接就可以鎖定參數(shù)問(wèn)題。
控制變量是進(jìn)行測(cè)試應(yīng)該遵循的首要思路 通過(guò)檢索,我們可以找到大量的相關(guān)信息,可能會(huì)存在各種五花八門(mén)的解決方法。如何判斷哪些值得嘗試哪些不值得嘗試非常重要。對(duì)于本文的 bug,即便檢索到了很多關(guān)于軟件版本的問(wèn)題,只要我在版本相同的另一臺(tái)電腦運(yùn)行成功,那么所有和 Java 版本,Windows 版本以及 IGV 版本相關(guān)的內(nèi)容都可以忽略掉,就省去了多次卸載安裝這樣的無(wú)用功。同時(shí)根據(jù)檢索信息再結(jié)合控制變量的思想就更加容易鎖定問(wèn)題,進(jìn)而找到解決方式。
|