下面以对KOEI的无双大蛇的一个bug的研究为例,介绍程序分析的基本方法。
无双大蛇是KOEI公司推出的一款动作类游戏,3.20日发布了日文PC版。
在游戏过程中发现,进入某些战斗的时候程序会陷入死循环,具体表现是在菜单中选择战斗场景之后,画面变黑,但是一直不出现战前配置的界面。Alt+F4关闭主窗口之后,还可以在任务管理器中找到它的进程。
因为是进入战斗场景时发生的问题,猜测可能是读取或者解析场景数据的时候发生了错误。因为无双系列的数据量较大,通常场景数据不会保存在内存中,而是直接从文件读取,所以先用SysInternals中的procmon工具对文件读写进行监控,以便定位场景数据。
监控之后发现程序在使用ReadFile一次性读取大量数据(>35M)的时候失败(procmon中给出的错误信息是insufficient resource),然后程序一直在重试,从而导致了死循环。
使用关键字“ReadFile "insufficient resource"”Google了一下,没有发现太有价值的线索。
我自己写了个测试程序读取相同位置相同数量的数据,没有任何问题。因此暂时无法准确定位读取失败的原因。
正好要对大蛇的主程序进行分析,所以也顺便研究一下这个bug。
首先,因为自己的测试程序可以成功读取,所以怀疑是读取参数的原因导致ReadFile失败。因为ReadFile本身通常不会带参数(虽然最后一个参数是用于指定overlap的,但是通常很少用到),所以先研究CreateFile调用时传递的参数。
很快定位到程序中打开数据文件的代码,发现它在CreateFile的时候传递了FILE_FLAG_NO_BUFFERING参数。MSDN了一下,里面只提到使用该参数需要注意一些对齐的问题。
修改了我自己的测试程序,加入了FILE_FLAG_NO_BUFFERING参数。这次终于重现了bug,查看GetLastError的返回值,发现确实是ERROR_NO_SYSTEM_RESOURCES。
再次使用Google,加入新获得了两个关键字FILE_FLAG_NO_BUFFERING和ERROR_NO_SYSTEM_RESOURCES,可以找到更多相关的链接。浏览之后,基本可以肯定是一次性读入过多的数据导致的问题。
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.kernel/2005-02/0489.html
因为问题实际上并不是FILE_FLAG_NO_BUFFERING参数导致的,所以我们去掉这个关键字。另外我觉得很多人应该会把该行为当成bug(因为MSDN上并没有提及),所以在关键字中加入bug,以期望进一步验证我的判断。然后重新搜索,得到了一个非常有价值的链接:
http://svn.icculus.org/physfs/trunk/lzma/C/Archive/7z/7zMain.c?view=markup&sortdir=down&pathrev=923
里面明确提到了这个问题
/*
ReadFile and WriteFile functions in Windows have BUG:
If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
(Insufficient system resources exist to complete the requested service).
*/
这说明,当碰到了bug,并且你已经掌握了较为详尽的出错相关信息的时候,可以直接通过Google的codesearch,或者koders网站在开源软件的代码中进行搜索,以充分利用前人的分析结果。(其实MFC的源代码注释中也有很多类似的信息)
当然最后修复这个bug就很简单了,自己写一个以4M为单位读取文件的代码,然后做个内存补丁即可。