分析:

  根据程序退出时打印出来的堆栈信息,找到了发生异常的代码位置。令人感到惊讶的是,这一行是绝不可能发生空指针的,因为它用的都是基本数据类型。上下几十行之内也是绝不可能发生NullPointer的。

  解决方案:

  这个问题,始终没有找到解决方案。

  案例二:

  背景:

  对一个系统做大规模的随机压力测试。一个对象的类Message是一个final的类,且其重载了toString()方法,它会按如下格式打印信息:"{ what=XXX when=XXX XXXXXX }"

  问题:

  在一次测试过程中,系统核心进程因异常退出,导致系统自动重启。

  分析:

  问题的原因是在核心进程中发生了一个RuntimeException,并有一条消息:"[c0x44bc: This message is already in use."。从堆栈找到退出的位置的代码,发现它是程序检测到不合理的操作然后抛出的一个RuntimeException:代码如下:

// ...
if (msg.when != 0) {
throw new RuntimeException(msg + " This message is already in use.");
}
// ...


  这里的msg是一个Message的对象。

  通常来讲+会调用对象的toString()方法,而toString()方法的输出又有特定的格式,所以,这里发生了让人极其迷惑的事情。因为终打印出来的消息跟对象的toString()有很大的差别。从日志信息来看,当前对象应该是一个char数组,而并非一个Message对象,但是在相关的上下文都无法找到这样一个char数组。

  解决方案:

  这个问题,怀疑是对象的内存已被破坏,其内的数据已不再是对象本身。这也是一个没有解决方案的问题。

  教训:

  这类问题是真正的难题。需要对语言和系统达到精通的人物才可能解的了。

  5、难重现的必现问题

  背景:

  一个Gallery应用程序能够以网格形式显示很多张图片。当图片较多时,会在左边出现滑块用来滚动屏幕。正常来讲,当打开应用时,这个滑块应处在上面的位置。

  问题:

  有人报告说初始打开应用时,滑块不是在上面,而是在中部,或其他地方。而且声称这是必现的问题。

  分析:

  但当调试的时候却怎么也无法重现这个问题。虽然这并不是一个严重的问题,但感觉这样的行为是很诡异的。在调试的过程中,发现,测试人员的应用与我这边应用显示图片的顺序似乎有些不同,是正好相反的顺序。这是有一个可配置的选项在控制的,所选项设置为正序或倒序。发现测试人员当时用的倒序,而通常大多数情况下,用的都是正序(可能是因为没有人去设置这个东西,它默认的是正序)。这可能是问题产生的原因。果然,当把顺序设置为倒序的时候,滑块的位置不会置顶了。

  解决方案:

  找到了问题的重现规律,对于这种问题好办了。发现,当设置为倒序的时候,代码的本意是想把滑块放在后,但是计算时有些错误,误把一个屏幕窗口的高度当成整个文档长度了。所以当,整个文档长度超过一个屏幕时,滑块的位置不正确了。由于,把滑块放在上端的更符合一般情况,所以无论排序顺序,把滑块置顶。

  教训:

  事实上,很少问题是真正的小概率事件(Seldom),只有当涉及多个线程的时候才会有真正Seldom的问题,因为无法确定线程的执行顺序。对于其他的问题,应该是都还没有找到重现的规律。相信了这一个事实以后,要不断的去试验和假设以重现问题。在试验的时候,也要注意细节,因为很多细节都可能是一条重要线索。重要的是要相信问题是存在的,是可以重现的,更是可以解决的。

  有一些方法技巧可以用来重现看似比较难重现的问题:

  1)仔细询问或查看问题出现时的相关操作和日志,以确定是否漏掉了一些必要的前提条件和操作

  2)如果涉及与其他应用或模块交互,则要了解每个应用和模块的特性,然后做适当的假设,再去做试验。

  3)猜测可能导致问题的原因,然后去创造这些条件,看在有这些条件的情况下,是否可以重现问题。比如,如果猜测是时间长短或文件大小导致了问题,那么可以调大时间,用大文件来测试。如果猜测是空指针的问题,那么可以故意创造出一个空指针等等。

  4)要相信问题是存在的,也要相信问题是可以重现的,更要相信这个问题是可以修复的。

  5)要有耐心,不断的思考,假设,然后去验证,一次改变一个条件,一点一点的分析与验证,终是能够重现并修改问题的。

  6、真正难重现的问题----线程问题

  背景:一个手机音乐播放器支持多个播放列表,当删除播放列表的时候,如果其中有正在播放的歌曲,那么当删除这个播放列表后,是不会再继续播放。在删除过程中,如果删除到正在播放的歌曲时,会打开并播放播放列表中的下一个歌曲,直到播放列表中的所有歌曲都删除完。删除歌曲的动作是放在与播放不同的线程。

  问题:有些时候,删除含有正在播放歌曲的播放列表后,当前歌曲跳到了其他播放列表继续播放

  分析:删除过程中会涉及到很多个过程:停止,找到下一首,播放,和删除,如果每一次都这么走的话,是不可能出现这个问题的。经过大量的调试与跟踪,终发现,是由于停止和播放没能一次执行完,中途被打断。因为删除是在另一个线程中,因此有可能当正在执行停止的时候,TaskScheduler切换了线程,执行了播放,从而切换到了另外的播放列表。

  解决方案:找到了问题的原因,解决起来很容易了,加上同步锁,让删除,停止和播放的每一个过程都保证不被中断,解决了这个问题。

  教训:这是一个真正的随机出现的问题,因为它是由于线程引起的。多线程带来的第一个问题是不确定性,另外一个问题是同步与共享的问题。如果没能很好的处理同步与共享,那么多线程会带来更多的问题,远多于它们所解决的问题。