All Stories

一次从栈中寻找被优化到寄存器中的局部变量指针的经历

一次从栈中寻找被优化到寄存器中的局部变量指针的经历

  上周基本解决了一个dump文件分析的问题。   一开始,问题没有弄复杂,只是从最后异常看到call stack,大体得出某个类的指针为NULL,却被继续使用,然后fix的话只要在被使用前判断一下是否不为NULL就行了。   不过这个fix有一点没有考虑到,是该方法一开始就有一个分支对该指针进行了判断。所以如果该分支确实成立的话,我的fix就说明有问题了。于是新的问题就成了验证该分支的条件是否成立。   分支的条件是判断另一个对象的每个成员是否为空。本来,这个对象一开始是new出来的,指针是某个方法的局部变量,一般说来是放在栈上的。不过通过call stack切换到那个方法的context后,发现查看局部变量并没有那个指针。通过查看反汇编的代码,才发现原来该指针被优化了,并不存储在栈上,而是存储在寄存器r13上了。于是我就开始想着如何可以查看某一级call stack时的寄存器,一开始我以为每次call指令时,会自动把所有寄存器的值都压栈的,后来发现不是,还特地去找了Intel的指令手册看了一下说明,反正没说有压栈寄存器,大概是我记错了。问了一下那个老外同事,他说没什么办法,只有想办法从栈里找。   这样没头绪了几天,后来灵光一闪,偶尔发现在call stack中使用r13存储指针的方法的下一级被调用方法的反汇编代码中,把r13压栈了!于是只要在栈中就肯定能找到那个指针的值了。至于怎么找那个栈中的位置,从函数的返回地址找!从最后的esp值开始大地址(栈的生长方向是往小地址方向生长)开始搜索那一个方法的返回地址,找到后再往小地址找压入的栈的寄存器值,果然找到一个值,可以通过dt来查看,还可以通过dds该地址的vf-table来查看它的虚表包含的虚函数名称,这样就可以验证是否是要找的那个对象。果然没错!

最近过得很开心

最近过得很开心

  前天傍晚下班回来的路上被追尾了。因为车速比较快,前面的一辆的士突然停下载客,我就急刹车,差点点就要撞上前面的,我都感觉到我的车好像因为刹车踩得太猛而什么东西突然断裂了一样,但是后面另一辆的士就没刹住,撞上我了。看了一下,我只是后面有几条划痕,那的士是右大灯完全废了,都往里陷入了。我也懒得跟他计较,只想着快点回去。   最近几天因为原来做面试的两个同事都到美国培训去了,于是把面试的活都仍给我了。开始还觉得有点新鲜感,两三次后就有点厌倦了,电话面试了好些人,不少还是硕士,不过貌似大多对软件开发并不是很熟练。   接到几个Watson bug,不过实在没经验啊。   KarenMeu迟迟不开工,主要是有两大块还没一个清晰的思路。界面部分想模仿Qt Creator,看了一点源代码,没头绪。另外就是想有一个极度灵活的插件架构,插件主要分两类,一类是界面插件,一类是业务插件,业务插件产生数据,提供给界面显示和操作。同一个业务插件可以让用户自由地选择不同的界面插件进行显示。具体细节想不好该如何实现。   最近过得很开心。

Ninayan W.I.P.(29)

Ninayan W.I.P.(29)

  这两天又重新拾起Ninayan来,随着embed.ly的收费,Ninayan的主打的图片、视频浏览功能被废掉了,只好自己写代码来实现这部分了,但这工作量应该比较大,只能慢慢来了,有点想把这部分代码做成开源项目,叫SANSASORI。   之前也有断断续续地修改,支持Follow5,后来Follow5停运,然后支持StatusNet。这两天主要修改了一下UI,Twitter部分原本就支持使用Twip4的T模式和O模式,只不过没有设置UI,这次把账号设置的UI做了小部分高速,除了可以修改API地址,还为每种oauth服务都增加了用户自定义API Key的功能。这主要是在支持腾讯和新浪微博的过程中,一直遇到因为API Key不能正常使用的问题,如果用户可以自定义,应该能缓解一部分问题。   另外,还在Proxy部分增加了导入用户自定义的SSL证书的UI,这样就可以使用goagent做代理了。不过使用goagent的过程中偶尔发现会有所有网络连接都阻塞不返回,然后不能发起新的网络连接的问题,在网上找了一圈没找到怎么为QNetworkAccessManager的post和get方法设置timeout的方法,纠结。   把文章、图片和视频的浏览功能修复后就不打算再为Ninayan大动干戈了,不增加功能了,顶多就是有bug修一下。之后就开始做KarenMeu了。

嵌入的CLR引用销毁的C++对象的问题续

嵌入的CLR引用销毁的C++对象的问题续

  前面的blog中,网友sali98提到可以用#pragma init_seg(compiler)等来安排全局对象的构造和析构顺序。这倒是我以前没有想到的,不过经过实际测试后,发现这个方案并不能解决我的问题。   在原来的定位结果中,以为单纯是嵌入的CLR对象晚于C++对象的销毁引起的问题,实际上问题要稍微复杂一点。首先,上次提到的使用_exit引起的对象无声息的消失,不是主要问题。这里调用_exit是在之前有个人把exit换掉的结果,他以为_exit不会调用对象的析构函数,于是不会引起析构时资源销毁时机不正确的问题。事实上这个fix是不起作用的,因为从call stack来看,_exit对嵌入的CLR似乎不起那个作用,而且无论是exit还是_exit都会在对象正常或不正常销毁后仍然有消息循环存在,消息循环中某些处理代码仍然会继续运行,而那些代码也是有可能会引用已消失的singleton的。   最早我想的解决方案是基于这样的想法,既然是C++的singleton在被销毁后没经检验就来使用,那么就把singleton改好一点就行了,我想过《Modern C++ Design》中提到的Phoenix Singleton,也看过一些boost中的Singleton的实现。不过后来都感觉太麻烦,没必要。于是今天试了一个很简单的方法,增加一个static bool变量,标记当前singleton是否有效,然后在每个非static的method开头都先判断一下。简单测试一下,倒是能勉强工作了,不过发现另外一个问题,这样的singleton不止一个!而且另一个不好的地方是,每个非static的method都要修改一下,不必要的改动太多。   今天又看了一下crash的call stack,我就觉得单纯修改singleton的实现似乎不是很高效。于是我就想,是不是有办法在调用exit或_exit前就自己写代码把CLR卸载掉,把消息循环停掉?以我对.NET的粗浅的了解,我觉得嵌入的CLR是应该可以主动卸载掉的,只不过也许某些正在使用的资源会成为阻碍,这个要仔细调研一下。而停掉消息循环这个,感觉也是可以的,增加个标记,在消息循环里判断一下,可以自己主动跳出循环。

编译了GDIPP

编译了GDIPP

  前两天才知道GDIPP是LGPL的,放在googlecode上。昨天把它所有的源代码clone下来了,然后编译了一下,倒是全部编译出来了,不过运行后貌似没什么效果,囧了,有空读一下它的源代码。众所周知Windows的字体渲染效果不怎么样,所以对于自己有一套效果良好的字体渲染手段是很有吸引力的,我就是想试一下把GDIPP集成到自己的程序中去。

用Intel C++编译Qt失败

用Intel C++编译Qt失败

  之前说过想用Intel C++编译Qt来着,于是从VeryCD上找到Windows、Mac和Linux的Intel C++安装镜象不辞辛苦地下载下来安装。结果让人很沮丧啊。   在Windows 7中,无论编译64位版本还是32位版本,都会编译失败,我怀疑是因为Intel C++用了Visual C++的头文件引起的问题,但是我直接用MSVC2010来编译64位的Qt,虽然也是编译失败,却跟Intel C++报的错误不同,好像C++11标准的问题,在Webkit部分中有类名为nullptr,这个名字在C++11中作为保留字了。也不知道那些用MSVC2010编译成功的是用了什么参数。至于使用MSVC2008的配置,我就没兴趣编译了,因为我不喜欢MSVC2008的SxS。   然后在Mac中编译,32位版本也是编译失败,64位版本编译倒是全部通过了,但貌似make install不完整,那些头文件就没有复制过去,还缺了些什么不知道,反正直接把Qt源代码目录中的include目录复制过去是不能用的。于是也放弃了。   后来么,在Windows上下载了32位和64位的TDM GCC,编译了两个晚上才编译出来,话说那个-nomake "demos examples docs"参数不起作用啊。试了试编译Ninayan 64位,可以运行。   这两天看到Nokia的Qt论坛上有个叫Qt4iOS的人,说他做的Qt for iOS插件就要可以正式发布了,好期待啊。又看到有人说,Necessitas虽然还在Alpha版本,但已经可用性很高了,我看了下,现在居然已经提供4.8.0版本的for Windows/Mac/Linux的SDK了。不过在Mac上安装好后想编译Ninayan才发现,源代码还需要做些修改,比如源代码中通过预定义宏只识别了Windows/Mac/X11/Symbian/Maemon,但是这个Android不知道是什么宏,不知道上哪去查点资料看看。   于是最近还在网上看到的消息,是说Windows Phone的下个版本会支持C++开发,如果这样的话,Qt也会很快就支持WP开发。啊,如果Qt真能用于Android、iOS和Windows Phone的开发,那就太牛逼了。说实话,如果这些port的质量可以的话,只要价格不是太离谱,我应该会买这license去的。

换用Intel C++编译Qt

换用Intel C++编译Qt

  春节放假时有一天突然想到Qt是支持用Intel C++编译的,有icc的mkspec,而且Intel C++在Windows、Mac和Linux都有,还能生成32位和64位的可执行文件,于是就想试一下,如果确实可行,以后就不用GCC了,虽然GCC也很好。   比较囧的是,在Windows上,Intel C++除了最核心的编译器,其他的都是用Visual C++的,比如运行时,甚至包括nmake,于是很不幸的是nmake貌似不能通过命令行参数指定并行编译,只能单线程编译了,纠结。   自己编译Qt的话,Windows和Linux都要为32位和64位系统分别编译,Mac倒是只要编译一个就行了,但是有个小问题是上次在Mac OS X 10.6.7上编译Qt 4.8.0时64位版本到后面有个什么库没有,于是只能编译32位版本了,囧。

春节过完啦

春节过完啦

  1月21日下午3点半就从公司走人了,到柯桥、绍兴路段堵了一会儿。   除夕,小丫头给我发短信说最近遭遇了很多很多不幸,跟做梦似的。她爸爸过世了,给人家干活时从屋顶摔下来。她自己流产了。我不知道自己可以做些什么。橙子在Facebook上说,还是放不下吗。我说,我想关心她,却没了以前的那种男女之情,倒有点像亲情。   春节的行程比较充实,初一中午在家接待干妈一家,晚上去干妈家。初二去大姨家。初三在家休整。初四接待大姨小姨,下午还去上坟。初五晚上去小姨家。初六,就是今天,到上海。   木有宽带的日子,真不习惯啦!

嵌入的CLR引用销毁的C++对象的问题

嵌入的CLR引用销毁的C++对象的问题

  今天彻底打酱油了,我们shared dev team也只剩下我,老大和Jason三个人了。因为晚上2点才睡,才睡了不到6个小时,于是下午就坐在办公椅上睡了近1个半小时,最后是被他们讨论一个bug的声音吵醒的,啊哈哈,老大还说让我看一下,现在只有我在这方面有经验了,我囧,我完全没经验的说,后来还是Sherman厉害啊!   再后来,就跟老大讨论了一会儿C++ singleton的实现,以及跨DLL数据引用等等。问题是有个Watson的bug,我从一次crash的call stack中发现,程序在调用_exit后,该程序中的static object应该是已经瞬间被无声息地干掉了,所谓无声息的,就是说,连它的析构函数都没被调用的。但这时嵌入的CLR还需要做一部分扫尾的工作,而恰恰是这扫尾工作又反过来调用了那个貌似已经被干掉的static object,于是程序crash了。当然这只是我的猜测,我猜测嵌入的CLR就是要生存周期长一点,于是一直在代码中试图找一下它是怎么从C++端嵌入CLR的,然后怎么用CLR的。我发现的情况貌似是这样的,先用Managed C++写了一个dll,这个dll可以在DllMain,还可以导出函数,而据我前些天才知道的知识,.NET编写的普通的DLL形式的assembly跟原本的DLL不一样,没有DllMain的。而这个DLL通过导出函数返回一个对象的指针,这个exe程序通过GetProcAddress获取导出函数,再调用这导出函数获取对象指针。这个返回的对象呢,是个CLR Bridge,也就是说,通过这个对象,可以从C++端创建CLR中的对象,调用CLR对象中的方法等等。也就是说,从代码中,我没看到Jeffray Richter在《CLR via C#》中说的那种CLR host的方法。现在我仍然在怀疑,是不是我代码没看全,但我确实之前也在整个代码目录下搜索文本,没有那几个用于host CLR的API调用。似乎有点跑题了。然后我就跟老大说了一下我发现的这些情况,略微讨论了一会儿,老大表示自己也不知道,唔,其实我也不指望他知道,只是有这么一种想跟人分享自己的发现的欲望而已。基本上,我就觉得这很可能是此bug的root cause了,但老大说可能只是个cause,而不是root cause,好吧,其实就是缺少验证而已。一个比较有说服力的验证方法是自己用C++写个小程序,然后用相同的方法调用CLR中的代码,最后能制造出同样的crash,只是我最近木有动力去做这些事而已。另外就是,即使确定了这是个root cause,简单地说来,这个root cause应该就是对象销毁的顺序不对,这是可以肯定的,但之后也不好fix,因为这个程序实在太庞大了,有很多对象,然后引用关系也很复杂,以我目前对它的了解程度,根本没能力对理顺这个关系,于是也就fix不了了。而且还有个另外的问题是,那个static object是该程序中用于实现singleton的一种方式,我觉得比较奇怪,老大说,这是为了应付多线程的情况。还有种应用多线程的singleton实现方式是在create instance时加锁,唔。关于这个话题,在前段时间看到TopLanguage group中有个讨论,提到boost中某个库中的singleton实现,貌似很干净的实现,不用锁,也不是static object,能适应多线程,囧,具体的不记得了,貌似boost中有好几个子库中都有自己的singleton的实现,得再去看看代码才行,另外好像《Modern C++ Design》里也有对多线程singleton实现的讨论,春节放假看看去。   话说,今天还看到Mono,发现除了有Mono Touch外还有Mono for Android,不过免费试用版都只能在emulator上跑,最便宜的个人版license也要399刀。不禁大骂Qt的不给力,为毛只能为Symbian和MeeGo用,Android port至今还在alpha...