一个回车引发的Bug
2011-03-13 22:13
113 查看
前两天解决一个cocos2d-x 解析XML出错的Bug(链接在此),满有意思,记录下来。
看到这个问题很是疑惑,这个测试例我们发布版本前测试过多次了呀,在Windows上跑,在Mac上跑,在PC上跑,在手机上跑,在Iphone上跑,在Andorid上跑的都好好的,还会有什么问题呢?
晚上,正好本本是Windows7,再来…Checkout代码…编译…运行…跑测试例…挂了。哈哈,能重现就好,开始Debug,调呀调、调呀调,嗯?libxml2解析失败了?难道Windows7上Checkout出来的XML文件和别人的不一样了?用VC打开XML文件,VIM打开XML文件,调试,打开变量调试窗口、打开内存监视窗口,我左看右看上看下看它每个字母都一个样。
到底是怎么回事呢?
貌似没什么问题?不管了,继续调试…下载libxml2的源码…编译Debug版本…替换工程中的库…单步调试解析XML文件数据…从解析第一句跟踪到最后一句…没问题呀…慢!都
数据解析完了,怎么还要继续读缓存中的数据?数据解析完了还继续访问,不挂掉才怪。
在VC IDE的变量监视窗口中输入变量名(pBuffer, 1117)细细观察(顺便说一句1117是ftell返回的文件字节数)…终于发现问题了:
ftell返回的XML文件的字节数是1117,但pBuffer中到第9百多字符处已经是文件内容的结尾了,后面都是分配出来后未赋过值的内存,数据都是不确定的值,libxml2解析到后面怪不得会失败。
比如有一个文本文件,里面存着字符串"abcde\n12345”11个字符(不算那个结尾0),在内存中占用11个字节,我们把它放到文本文件中,这个文本文件的数据有多少字节呢?
11和字节或者12个字节都有可能,因为在文件中回车可能是 "\n”(Linux)、"\r”(Mac)或者"\n\r”(Windows)。当fread在Windows上用"rt”模式读取文本文件时,它会自动把"\n\r”转换为"\n”。
所以,按上面的代码逻辑:根据ftell返回的文件长度,分配了12个字节的缓存,fread读取整个文件数据,填充了前11个字节,交给libxml2解析的数据为"abcde\n12345?”(?为未赋值的内存),当解析到?处发生了解析失败。找到原因了,解决问题就简单了:
编译…运行测试例…通过…收工。
为什么在WindowsXP上运行就没问题呢?
迷雾重重
问题是这样的:有引擎的用户在论坛上发帖说引擎在Windows7上跑一个需要从xml格式的游戏数据的测试例时程序会crash掉。看到这个问题很是疑惑,这个测试例我们发布版本前测试过多次了呀,在Windows上跑,在Mac上跑,在PC上跑,在手机上跑,在Iphone上跑,在Andorid上跑的都好好的,还会有什么问题呢?
山穷水尽
不猜了,还是Debug先,嗯,公司开发环境只有WindowsXP,没有Windows7,既然都是Windows,那就在XP上跑跑先。Checkout代码…编译…运行…跑测试例…一切正常……晚上,正好本本是Windows7,再来…Checkout代码…编译…运行…跑测试例…挂了。哈哈,能重现就好,开始Debug,调呀调、调呀调,嗯?libxml2解析失败了?难道Windows7上Checkout出来的XML文件和别人的不一样了?用VC打开XML文件,VIM打开XML文件,调试,打开变量调试窗口、打开内存监视窗口,我左看右看上看下看它每个字母都一个样。
到底是怎么回事呢?
柳暗花明
按道理libxml2这么流行的解析库不应该有问题呀,翻翻代码:......
FILE * fp = fopen(pFilePath, "r"); // 打开文件
fseek(fp,0,SEEK_END); // 到文件尾
unsigned nSize = ftell(fp); // 获取文件长度
fseek(fp,0,SEEK_SET); // 返回文件头
unsigned char *pBuffer = new unsigned char[nSize]; // 分配数据缓存
fread(pBuffer,sizeof(unsigned char), nSize,fp); // 读取文件内容
......
xmlSAXUserParseMemory(&saxHandler, this, pBuffer, nSize); // 解析文件内容
...... // 有没有发现问题?
貌似没什么问题?不管了,继续调试…下载libxml2的源码…编译Debug版本…替换工程中的库…单步调试解析XML文件数据…从解析第一句跟踪到最后一句…没问题呀…慢!都
数据解析完了,怎么还要继续读缓存中的数据?数据解析完了还继续访问,不挂掉才怪。
在VC IDE的变量监视窗口中输入变量名(pBuffer, 1117)细细观察(顺便说一句1117是ftell返回的文件字节数)…终于发现问题了:
ftell返回的XML文件的字节数是1117,但pBuffer中到第9百多字符处已经是文件内容的结尾了,后面都是分配出来后未赋过值的内存,数据都是不确定的值,libxml2解析到后面怪不得会失败。
迟来的结论
为什么ftell返回1117个字节,而fread只读取了9百多个字符呢?用VIM打开XML文件,转换为16进制仔细察看,终于发现了问题:问题出在回车上。嗯,用调试的例子解释太麻烦了,换个简单的说法:比如有一个文本文件,里面存着字符串"abcde\n12345”11个字符(不算那个结尾0),在内存中占用11个字节,我们把它放到文本文件中,这个文本文件的数据有多少字节呢?
11和字节或者12个字节都有可能,因为在文件中回车可能是 "\n”(Linux)、"\r”(Mac)或者"\n\r”(Windows)。当fread在Windows上用"rt”模式读取文本文件时,它会自动把"\n\r”转换为"\n”。
所以,按上面的代码逻辑:根据ftell返回的文件长度,分配了12个字节的缓存,fread读取整个文件数据,填充了前11个字节,交给libxml2解析的数据为"abcde\n12345?”(?为未赋值的内存),当解析到?处发生了解析失败。找到原因了,解决问题就简单了:
nSize = fread(pBuffer, sizeof(unsigned char), nSize, fp);
编译…运行测试例…通过…收工。
为什么在WindowsXP上运行就没问题呢?
相关文章推荐
- 一个存在三年的内核 bug 引发大量的容器系统出现网络故障
- 转发同事总结:一个BUG引发的血案(经过篇)
- DynamicDataExchange(DDE)机制引发的卡死有一个bug
- Erlang 程序引发共享内存 bug 的一个例子
- 通过View.post()获取View的宽高引发的两个问题:1post的Runnable何时被执行,2为何View需要layout两次;以及发现Android的一个小bug
- MySQL 5.6的一个bug引发的故障
- Nutch学习笔记10---一个bug引发Http协议研究
- C++析构函数自动调用,引发的一个bug
- 一个 Bug 引发的思考(超赞的文章)
- 一个回车引发的几夜不眠
- 一个bug引发对小程序运行原理的思考
- 表单提交后分页页面标签POST实现 —— 一个小小的bug引发的思考
- 剖析一个由sendfile引发的linux内核BUG
- 『转』度百死去飞秋一个BUG引发的血案
- 一个使用method_missing()容易引发的bug
- 一个BUG引发的思考
- 【Android源码阅读系列一】一个bug引发的血案:阅读Android源码 MeasureSpec类(API版本:23)
- 一个Date对象引发的诡异bug
- [置顶]Win2012R2的一个Bug安装群集后可能引发的软件崩溃问题及相应补丁
- 一个bug引发的思考 --- ASP.NET页面加载顺序讨论