嵌入的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 3,beta和rc都遥遥无期,更别说正式release了。而iOS port则压根貌似没人做了,叹气。我在想,如果Qt现在如果有Android和iOS的port像现在的Mono那么高的成熟度,我说不定真会去花这钱买license,囧!

打印设置bug几乎搞定

  把Charles Petzold的关于打印的那章代码拿来都试了一遍,发现PopPad工程里的过程就是我想要看的。对比了一下代码,沮丧地发现貌似两者只有获取到DC的方法不同,PopPad里是PrintDlg返回的,而bug里的代码是通过CreateDC创建的。但之后也发现了,这个代码很奇怪啊,不但有CreateDC,还有CreateIC,经过调试发现,大部分时候都是在调用CreateIC的,这让我纠结了好一会儿,想不明白为什么要调用CreateIC,MSDN上明明说的CreateIC返回的DC是不能用来画东西上去的,只能用来查询信息的。又经过几次调试,发现CreateDC是在打印前在调用了n次CreateIC后最终会被调用一次的,这时候才发现,传给CreateDC的DEVMODE*居然里面的dmCopies值一直是1,而我明明需要的是2啊!于是我就猜,是不是application层把明明是2个copies合并成1个了,然后相当于只打印1个copy,于是打印机的设置只对第1份copy起作用。因为这个猜想,又小小地郁闷了一把,该不会要去调试application层的X++代码吧。不过马上,在kernel层的call stack乱翻,翻到其中一个地方,硬是把所有dmCopies值都改成1了!把这行代码注释掉试了下,果然如愿可以把设置应用到第2份copy上了。我猜,当时这代码的作者大概是考虑到不是所有的打印机都支持multi copies的,所以干脆把这个字段都改成1了。然后就是写邮件啦啦啦啦啦!
  这事一了,人突然就松懈下来了。

继续bug fixing

  又回到以前那种每次写blog都是写工作内容的流水帐的状态了么,这是不是意味着离我辞职又不远了。
  好吧,上午还是在继续折腾SQL Server版本兼容性的bug。主要的代码昨天就修改完了,今天要提交code review,就再仔细检查了一遍,然后发现,因为多加了一个.cs文件,于是在enlistment的根目录下build所有工程的时候,某一个工程会出错。之前加了这文件,是直接在某个目录的sources文件,嗯,类似于makefile的一种文件里,添加了新增加的那个.cs文件路径就好了。于是在整个enlistment里搜遍了所有sources文件里的内容,愣是没在其他sources文件里找到有引用这些文件的。后来看了一下error log,发现它是在进入某个目录build时报的错,到该目录下又不死心地看了下sources文件,没找到要添加文件路径的地方。后来看到一个.csproj文件,打开看了一下,死马当活马医,把新文件路径加进去再build,居然通过了。艹,这个build system太贱了吧,不同的工程用不同的描述方式。
  下午继续折腾打印设置不生效的问题。好吧,一点头绪都没有,一点进展都没有,跟几个同事讨论了下,几乎也没有有价值的提示。现在还剩下两件事可以做,一是试试其他report的打印有没有这个问题,这可以基本上确定root cause是在application层还是kernel层,大体上我现在是比较倾向于认为是kernel层的问题,大概就是打印API使用的问题。二是自己写个打印demo,仔细观察一下打印API的工作方式,也就是通过这个demo来模拟bug的运行流程。
  好吧,我又无聊了,又写工作上的事了。

bug fixing, team building

  今天可是过得很纠结,两个bug开始全都一愁莫展。一个是打印设置的bug,搜索了一下以前的相关bug,看现象跟我现在的这个很类似,结果人家只是改了一下DOCINFO中的datatype字段的取值就搞定了,到我这里就不知道该怎么进行下去了。下午就在折腾另一个bug,安装程序在安装extension时只检测了SQL Server 2005和SQL Server 2008,于是在只有SQL Server 2012的机器上进行不下去了。需要修改的代码还是有好几处地方,照样画葫芦地改了一下,结果拿给人家测试时因为强命名的问题折腾了近一个下午。这里顺带提一下,如果把assembly拿到64位的Windows上去测试,用sn添加验证入口时不光要用32位的sn添加一遍,还要用64位的sn添加一遍。而且要注意的是,这个sn运行是在测试机器上运行的,不是开发机上。后来才知道,原来tester们有一个job跑一下,一台测试机就可以彻底屏蔽掉强命名的验证的,叹气。不过还好后来在在下班前终于编译了个private build让tester去测试一下,晚上回住处后看了一下公司的邮件,tester说在32位和64位机器上都测试通过了,稍稍心安了一点。
  晚上team building,在蜀香村吃饭,算是农历年年前的散伙饭。Fiona也来了,她在元旦前辞职后就没见过了,估计这次也是最后一次见她了吧。她比我稍微晚一些日子进的team,实际上她是build team的,接触并不多,只有过两三次因为build上的问题打过点交道,但感觉是个很nice的人。很瘦,说话轻声轻气的。

WinDBG使用进展

  从昨天下午开始check in,到今天下班仍然没有完成,太囧了,什么乱七八糟的东西,叫了组里最有经验的同事,还有build team里的妹汁来定位,仍然没有搞定,昏特了。
  倒是另一个分给我的bug,貌似快要被我搞定了。这次这个bug问题出在C++写的内核里,用Visual Studio 2008调试经验死掉,于是刚好用WinDBG来调,除了有点不习惯,其实WinDBG好像稳定多了,也快多了。其实一般在Visual Studio里调试,也就用到断点,单步,查看变量这三板斧,即使一点没有WinDBG的基础,也可以很快transfer过去。然后用Source Insight看代码写代码,好happy!只是如果仅仅用WinDBG做这些事,有点暴殄天物了。可是我不会其他高级用法,唔…
  话说上周五的时候让我给team里的人做WinDBG的training,然后我就随便写了几页ppt,忽悠了50分钟,只提到了怎么用WinDBG实现VS中的那些常见的调试任务,至于WinDBG最擅长的memory corruption,resource leak和postmortem debugging全都没提到,然后老大很不满意,唔,其实我这是故意这么安排的,虽然我自己确实对后面三个topics也没有研究,但要循序渐进嘛!

准备培训

  长假后第一天上班,恶补了下.NET Data Access、ADO.NET的基础知识,叫我这样一点都不懂的人给人去培训,汗。今天看《C#高级编程》看了40页,明天继续看XML相关的内容,也是40页。
  然后是安排在下下周五给自己team来个WinDBG的培训,这也是赶鸭子上架,其实我现在的水平,只是比他们多看了一两本书而已,实际能力跟他们差不多,只不过是让我负责这一块而已。我估计着这块的内容可以讲两到三次,每次两个小时,大体上可以分为基础知识,事后调试以及结合本项目调试。

任务多起来了

  今天快下班的时候,老大才告诉我,昨天让我看的那个bug之后随时都会assign给我处理,我囧,我一直以为老大是看我闲着没事干才让我看着玩的,叹气,形势认识不清!
  然后么手头一直有一个watson的bug,虽然这类bug是没有时间限制的,但确实没有头绪,如果一直没有fix,总感觉不太好。
  昨天么老大还说跟我聊聊,然后说可能马上会让我抽出一部分时间去做培训讲师,囧。一个现实的问题是,公司确实招不到顶尖的优秀毕业生的,所以只好自己派人去,给那些有意向的学生做相关的技术及soft skill的培训,并筛选出符合要求的人来。原来公司是在花桥有个培训基地的,不过现在听老大的意思,不太赞同那个跑去那边培训的方式,好像就只在公司这边搞一下。
  今天旁边的同事接到一个bug,跟TTT(Time Travel Tracing)相关,老大就跟我说这是你的一亩三分地啊,我表示听说过这玩意,老大觉得很诧异,这是MS内部的工具啊,我说我一两年前在某个google group上看到有人讨论过,说gdb也有类似的技术。其实啊,我现在倒真想好好学习使用WinDBG,甚至不用VS的JIT debugger,据说高手们,呃,大概只是MS内部的那些变态们,是不用VS JIT debugger的,比如现在我们对口的FTE那个印度佬,据说以前是IE开发组的,声称调试只用WinDBG。叹气。
  goagent貌似只是浏览网页表现好啊,我把gtalk的代理设置成goagent就经常连不上。Ninayan也用不了goagent。