IGV 哐当就不能用了,我的 debug 思路

IGV 哐当就不能用了,我的 debug 思路

不知道是哪一次更新,不知道是因为更新了什么,PC 上的 IGV 突然就不能用了。不能用了,怎么办。文章记录了我的debug过程以及一点思考。

最近一个多月有一件事情一直萦绕在心头,今天算是初步解决了,记录一下。

IGV 这个工具因为是 Java 写的,又因为 Java 全平台适配,一般不太容易出现 bug。在Windows 上常见的问题一般就是由于Java 32位和64位造成的。在64位的电脑上安装了32位的 Java(通常默认就是32位),如果给 IGV 分配的内存超过了 2G,就会报内存错误。最直接的方法就是把 Java 升级到64位就可以了。

然而,我不知道是哪一次更新,不知道是因为更新了什么,自己 PC 上的 IGV 突然就不能用了。如果是因为 Java 可用内存的原因,用官方提供的IGV.bat 通过cmd启动IGV是根本无法打开,但是我的 bug 表现是IGV可以正常启动但启动后只要进行任意一次点击就会闪退。

最初我的初步猜测是不是因为某一个内容的更新,导致了Java,Windows和 IGV 三者不兼容。于是我首先分别更新了最新版的 IGV 和最新版的 Java,然后还升级过一次电脑系统,不过问题都没有得到解决。

当时如果时间有限,debug 的最直接方式就是绕开bug。

替代方案

必须要用的软件突然不能用了又着急用,应该怎么办。立刻买个Mac应该就可以避免Windows上的问题了,但是对于我目前的状态来说并不现实。所以只能立刻寻找替代方案,因为 Java 本身是全平台,我在服务器上安装了 IGV linux 版本,然后通过 Xming 在本地电脑上调用Linux 开启的 IGV 图形操作界面。

这个方法给我解决了燃眉之急,不过从服务器通过xming 在本地进行点选操作,很多操作会用明显的卡顿和延迟而且分辨率实在是低。虽然解决了马上就要用的问题,但并不是一个持久的方法。这个过程大概维持了一个月,直到最近今天又需要重度使用 IGV,硬着头皮也要把问题解决一下。

dubug 过程

我的相关 IGV 报错信息

#
# 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.
#

详细阅读报错信息

因为我本身不懂 Java,只能根据报错信息提取自己认为关键的内容进行检索。

从以往的经验来看,首先要重点关注是哪里失败或者不能启动。在log文件的header部分恰好出现了一句话「Failed to write core dump. Minidumps are not enabled by default on client versions of Windows」,我认为这个信息应该非常关键,看样子很可能是因为 Windows 不可以写入 core dump 导致的,应该首先开启 core dump 试试。

SO 上查到了core dump 的开启方式,对于Java 8 使用 -XX:+CreateMinidumpOnCrash 即可。修改IGV.bat 文件再命令行里添加该参数后重新运行,问题依旧存在,但是这次除了会生成 log 文件以外,还会生成一个大小有几 G 的 core dump 文件。看来这个问题并不是我 IGV 闪退的原因。

在检索的过程中,有中文帖记录了出现这样的问题是因为「JRE的 version 和 JDK 不一样。所以,重新下载一个版本对应的JDK或者JRE就可以解决了」。虽然我其实分不清jre和jdk有什么区别,而且也确定自己是不是同时装了这两个东西,但是抱着试试又不会挂的心态,我还是再一次卸载了电脑里的Java。

这次因为担心是之前 Java 升级有问题,我还专门下载了官方的Java 卸载工具,想把自己电脑里所有和 Java 相关的东西都卸载个干干净净,重新来过。

按照 IGV 网站上给的 Java 8 下载链接,我又一次重新安装了 Java,运行之后竟然没有出现闪退的情况,因为根本就打不开了。其实这就是因为 IGV 网站上的 Java 下载链接引导我下载了32位的Java版本,但是 IGV.bat 中使用的内存配置是4G,超过了限额。我又不得不再一次卸载32位重新安装了64位Java,闪退问题依旧存在。

不过,至此我确认了自己的电脑不存在所谓的「JRE和JDK版本不一致的问题」,信息如下:

# 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 一个非常重要的经验和做实验一样是需要 控制变量。如果有条件,不妨找一台能够正确运行某个程序的机器,看看它和自己报错的机器有什么差别。

为此,我分别测试了实验室不同的几台可以正确运行 IGV 的PC,发现有人用的IGV版本较低,有人用的Java 版本较低,但是大家都可以正常使用,于是我又分别测试了几个低版本的Java或者IGV,然并卵。

考虑个人的笔记本系统和软件版本与我的工作 PC 高度一致,我又用最新版的IGV和Java 8在笔记本上进行了测试,神奇的是在笔记本可以正常运行。

至此,我基本懵逼。

因为Windows 系统和IGV版本以及Java版本完全一致的情况下,我的笔记本可以正常运行IGV,但是PC却不可以。这里面一定有某些不可告人的力量在操控。

理解报错log信息

通过上面几步折腾,问题的关键应该不在于解决 「Failed to write core dump」而是再向前回溯,这个时候就要理解 Java 生成的log文件究竟传达了哪些信息。

通常只有一个严重的错误引起Java进程非正常退出,出现了Crash,才会产生一个文件名为err+pid number 的日志文件。

EXCEPTION_ACCESS_VIOLATION (0xc0000005),检索后发现很多 Java 的报错都会出现这一句话,他意味着Java应用Crash时正在运行 JVM 自己的代码,而不是外部的Java代码或其他类库。在这个位置还可能出现SIGSEGV(0xb)或者EXCEPTION_STACK_OVERFLOW这样的内容。

再往下看另一个重要信息点是

Problematic frame:
# C [atig6pxx.dll+0x9e54]

这里的信息是显示 Crash 时JVM正在从哪个库文件执行代码。我遇到问题是 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

接下来我就阅读了一下 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.

仔细阅读之后发现基本等于白读,但是现在从报错信息中我已经完全明确了问题指向 atig6pxx.dll

指果所因

既然定位到了atig6pxx.dll ,就要查查它是个什么,搜索atig6pxx.dll windows 10。在前面几条结果中,我看到了这样几个信息

  1. AMD Graphics Driver not working/incompatible with Win 10 Technical Preview
  2. OpenGl for crimson drivers

如此看来,这个 atig6pxx.dll 和AMD 的显卡驱动有关,而我的两个显示器之一显卡确实是AMD的,从查询结果来看,AMD 显卡驱动的这个 atig6pxx.dll 和某些win10 版本确实存在问题(有可能我用的就是有问题的版本?)。

接下来再试试 atig6pxx.dll Java的搜索结果,真的还搜到了相关的一个帖子,官方给的回复如下,就是要详细确定显卡和升级啊各种信息,看来这个问题确实是 Java 和 PC 的显卡驱动有bug。同时又联想到我的控制变量实验,笔记本是NVIDIA显卡而不是AMD,进一步让我相信问题可能真的出自 AMD 显卡上。

Crash is reported with JDK 8u161 in Windows 10. From the stack trace it appears to be a driver issue.

Writing back to the submitter seeking additional details to

解决问题

基本决定了bug的原因,接下来就要考虑如何解决问题。

首先我需要知道 Java 和显卡驱动怎么能联系到一起,于是检索一下Java graphics看看,找到了 官网的一些说明。 这里主要涉及Java 2D 这么一个东西,在搜索的过程中又找到了 Java 2D 的options。在检索的过程中,还从SO看到了这样一个帖子,其中一个建议也是对显卡进行一系列操作,其中提到了一个参数-Dsun.Java2d.d3d=false

这个参数似乎有一点点眼熟,看到这里就要回过头重新看看IGV.bat 文件了。

官方提供的IGV.bat 实际命令如下

::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 %*

仔细看了一眼,官方这条命令实际调用的程序就来自 lib 目录的 IGV.jar,只不过添加了几个平时并不常见的参数,其中-Dsun.Java2d.noddraw=true和之前SO中建议添加的参数非常类似,而这个参数又是一个和显示性能相关的参数。隐约感觉问题就出在这里。

继续贯彻控制变量的思想,首先不加任何参数在 lib 目录下双击直接运行 IGV.jar,这次竟然没有闪退。回到 bat 依次修改上面三个参数进行测试,直到我把-Dsun.Java2d.noddraw=true 改为false后,闪退bug终于消失了。

至此问题得以解决。

复盘及与反思

综上,通过学习log文件,查找关键内容,控制变量测试,一步一步把 Java 的运行错误和显卡联系起来。可是谁能想到这错误竟然是和显卡相关呢?

通过进一步的检索,了解下Dsun.Java2d.noddraw这个参数的作用

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.

更进一步的说明如下:

The default pipeline on the Windows platform is a mixture of the DirectDraw pipeline and the GDI pipeline, where some operations are performed with the DirectDraw pipeline and others with the GDI pipeline. DirectDraw and GDI APIs are used for rendering to accelerated offscreen and onscreen surfaces.

  • Disable DirectDraw pipeline:

    When DirectDraw is disabled, all operations are performed with GDI. Provide the following flag to disable the use of DirectDraw: -Dsun.Java2d.noddraw=true. In this case all offscreen images will be created in the Java heap, and rendered to with the default software pipeline. All onscreen rendering, as well as copies of offscreen images to the screen will be performed using GDI.

  • Enable DirectDraw pipeline:

    In case the pipeline was disabled by default for some reason, it can be enabled by providing the -Dsun.Java2d.noddraw=false flag to the VM.

    However, typically there was a reason why it was disabled in the first place, so it is better not to force it.

也就是说,因为AMD 显卡驱动中atig6pxx.dll的问题,我目前的电脑并不能只使用GDI来进行渲染,而是必须要开启DirectDraw。至于更深次的原因没有深究的必要。

回顾整个 debug 的过程,虽然我一直在尝试使用控制变量的思路,但是具体的执行顺序不对。使用别人的电脑来控制变量,不相关的因素实在是太多,所以各种软件版本的不一致只是不一样的表象,并没有指明到是真正的显卡差别。而使用自己的笔记本和台式机做比较,其实已经排除了软件版本的关系。

最后才进行的控制变量测试其实才是变量最可控的最应该首先进行的,也就是在同一个电脑上对同样的软件进行不同的操作。其中一个操作是官方并不推荐的直接运行lib中的IGV程序,另一个操作是运行官方推荐的添加了三个参数的IGV.bat 。如果把这一个测试提到第一步来做,就会节省大量的测试时间。


最后,对于 debug 的一些建议如下所示:

  • 仔细阅读报错信息确定关键词

    成熟的工具一定有着非常详细的报错信息,而我们要做的就是从详细的报错信息中提炼关键词进行检索

  • 按照从小到大的顺序尽量找到一个可以正确运行的方式

    想要找到问题所在很关键的一点是谁或者哪里成功了。这一步对之后的 debug 过程十分重要。

    所谓从小到大的意思是正确运行和错误运行之间的差异越小越好。最好在自己的一台电脑上完成,只是改变软件的运行方式或者参数;如果不行就找配置尽量类似的电脑或服务器进行软件及版本的比较,越相似越好,以此类推。如果我首先能想到抛弃官方的建议打开方式,直接运行 jar 程序,直接就可以锁定参数问题。

  • 控制变量是进行测试应该遵循的首要思路

    通过检索,我们可以找到大量的相关信息,可能会存在各种五花八门的解决方法。如何判断哪些值得尝试哪些不值得尝试非常重要。对于本文的 bug,即便检索到了很多关于软件版本的问题,只要我在版本相同的另一台电脑运行成功了,那么所有和 Java 版本,Windows 版本以及IGV版本相关的内容都可以忽略,就可以省去很多卸载安装这样的无用功。

    同时根据检索信息在结合控制变量的思想就更加容易锁定问题,进而找到解决方式。


本文作者:思考问题的熊

版权声明:本博客所有文章除特别声明外,均采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×