1、与其他应用交互的Bug

  背景:

  一个手机音乐播放器,多媒体通常存放在手机外部存储卡上(SDcard上)。所以只有当SD卡Mount到手机上时,才可以播放媒体。音乐播放器会监听SD卡状态,当SD卡从手上卸载或弹出时,播放器会保存现场并停止播放;当SD卡重新Mount回手机时,再恢复现场和继续播放。也即当播放器收到“SD卡Eject”消息时,停止,当收到“SD卡Mount”时继续播放。播放器可以在后台播放媒体。

  一个文件管理器是另外一个应用程序,可以用来编辑(如删除)SD卡上的文件。文件发生变化后,这个第三方应用会发出”SD卡Mount“这样的消息,以便告知其他应用和系统,文件发生了变化。

  问题:

  当播放器在后台播放媒体的时候,起动文件管理器,然后随便删除一个文件(也可以是正在播放的媒体文件),这时候,播放器停止播放媒体,并且当打开播放器时,发现播放器是处于播放状态的,但是却没有声音。

  分析: 播放器不会没有理由的去停止正在播放的媒体。查找发现,播放器停止播放的原因有几个:用户请求停止,切换媒体时,SD卡Eject,SD卡Mount时。经过调试和跟踪日志信息,发现前面三种情况都未发生。那么问题的原因是SD卡Mount时,进行了恢复现场的操作,导致媒体停止了。那为什么会进行恢复现场的操作呢,因为SD卡并未被Eject和Mount。进一步调试和跟踪日志发现,当文件管理器删除文件后会发出“SD卡Mount”的消息,以便告知其他应用文件发生了变化。

  解决方案:

  当分析出了问题出现的原因,解决起来容易了,只需要让播放器在做恢复现场的动作前多一些检测来保证,先前SD卡有Eject过,现在确实是SD卡Mount回来。否则,应该忽略这个消息。

  教训:

  这应该是软件缺陷引起的Bug,音乐播放器本应该做这样的条件检查。音乐播放器本身的策略是没有错的,当没有其他应用程序发出这样的消息时,它是完全可以正常工作的。这是一个典型的与其他应用或模块交互时产生的Bug。

  对于这类Bug,重要的线索是,当与其他应用一起操作时会出现问题。这个Bug比较简单,因为很明确是与文件管理器有关。但在有些关系复杂的系统中,当出现了问题,很难搞清是表现问题的模块出了问题,还是其他模块出了问题。这需要调试跟踪日志,以缩小范围。在调试的时候要一个一个模块的试验和排除,以缩小范围和进一点的深入调查。

  2、原因与问题离的很远的Bug

  背景:

  一个模块专门负责处理SD卡上的多媒体文件,解析这些文件,获取它们的元信息,并存入到媒体数据库中。它是由Java和C++以JNI方式组合来实现的。Java进行上层流程控制和写入数据库操作;C++负责解析文件获取文件元信息。它们之间通过JNI来通信。

  问题:

  当处理一个特殊的文件时候Java的JVM(虚拟机)会异常退出(JVM abort),并打印出一条错误信息:“JNI Warning: illegal start byte 0xb1”.

  分析:

  这是一个很严重的错误,因为它会导致JVM崩溃,进程也会被内核杀掉。的线索是JVM崩溃时打印出的一条信息“JNI Warning: illegal start byte 0xb1”.通过搜索这条消息(幸亏有所有的源代码),发现它是由JVM内部的CheckJNI.c文件打印出来的。这个文件是JVM中比较重要的一个文件,它负责对JNI的所有参数进行合法性检查。特别是字串。因为Java中用的是Modified UTF-8编码格式,所以CheckJNI.c文件会对所传进来的字串进行编码检查,如果字串不是一个合法的Modified UTF-8格式,会发出警告并停止JVM。这里找到了问题所在,是因为JVM检查到了不合法的字串才导致JVM崩溃的。那么不合法的字串是从哪里来的呢?又是出现在哪个模块呢?因为系统中有无数地方在用到JNI,所以不能盲目的去查找。又由于这是在媒体扫描器扫描某个媒体时候出现的,因此主要目标锁定在媒体扫描器的JNI部分。媒体扫描器的C++部分解析多媒体文件,然后把所得到的结果(通常为字串)通过JNI传回给Java层。问题很有可能出在这里,因为多媒体文件的信息有多种编码格式,其中元信息有可能是非法的Modified UTF-8编码格式。经过调试跟踪,发现,确实是这里出现了问题。

  解决方案:

  这个问题的解决方案仍不是很好,一种方法是对其进行编码转换,但这要知道字串原来的编码方式。另一咱简单的方法是在传给JNI之前做一次编码合法性检查,以过滤到不合法的字串。后,采用了后一方式解决了这个问题。

  教训:

  首先,系统崩溃时所给出的信息和出错时给出的信息是第一重要的线索,虽然它们可能不是问题真正的原因,但是从它们出发可以追踪到原因。其次,错误信息,是由代码打印出来的,所以当你不知道是哪个模块出错时,可以用错误信息对源码进行局搜索,可以定位出模块和源码位置。后,如果是一系列的原因导致了一问题,那么可以在源头解,也可以其中的某一个环节来解,只要不会导致程序崩溃即可。

  3、空指针异常NullPointerException

  背景:

  在C/C++/Java中空指针异常是比较常见的一类导致程序崩溃的原因。在C/C++/Java中,如果使用的指针或对象没有正确的初始化,则很容易发生NullPointerException。

  问题:

  当发生NullPointerException的时候,程序通常会因异常而崩溃的。但通常都会打印出运行时的堆栈信息。

  分析:

  从程序的堆栈信息,会很容易的看到发生问题的代码位置,这样可以找到直接原因。但是这找到问题的一小部分,具体是什么导致对象为空,这不是那么容易调查出原因了。

  解决方案:

  对于这类问题,一开始能想到的办法是加上对空指针的检测,如果指针或对象为空的话不对其进行操作。但这是行不通的,这也不是正确的解决方法,直接的问题是,当指针为空的时候应该去做什么。如果在一个类中,在其他地方引用的时候都做了空指针检测,而这个地方没有做,那么可以仿照其他地方那样,加上空指针检测。但假如不是这样的情况,要好好的调查一下指针为什么会是空,而非处理空指针。但这通常都是比较困难的,因为要去追踪对象是从哪里来的,又在哪里被修改和引用,是在哪里初始化的。只有找到了真正让对象为空的原因,才能算是比较完整的解决了问题。但如果一个比较复杂的系统,引用的地方很多,且假如又涉及到多线程时,则追踪起来会更加的困难。

  教训:

  对于空指针问题,不能简单的加上一个条件。要进一步的深入去调查是什么导致了指针为空。除非,你有充足的理由去加上条件。

  4、无解的问题

  案例一:

  背景:

  有些问题是极及诡异的,而且出现的机率非常的小,但它们还是会出现,但是找不到合适的解决方案。

  问题:

  在一个GUI系统中,在一个比较基础的类里面报出了一个NullPointerException。由于这个类会被很所有涉及GUI的应用程序所使用。