<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type='text/xsl' href='http://tangl99.spaces.live.com/mmm2008-07-24_12.50/rsspretty.aspx?rssquery=en-US;http%3a%2f%2ftangl99.spaces.live.com%2fcategory%2f%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1%e7%ae%97%e6%b3%95%2ffeed.rss' version='1.0'?><rss version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:msn="http://schemas.microsoft.com/msn/spaces/2005/rss" xmlns:live="http://schemas.microsoft.com/live/spaces/2006/rss" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:cf="http://www.microsoft.com/schemas/rss/core/2005" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Tang Liang's notebook: 程序设计算法</title><description /><link>http://tangl99.spaces.live.com/?_c11_BlogPart_BlogPart=blogview&amp;_c=BlogPart&amp;partqs=cat%25E7%25A8%258B%25E5%25BA%258F%25E8%25AE%25BE%25E8%25AE%25A1%25E7%25AE%2597%25E6%25B3%2595</link><language>en-US</language><pubDate>Mon, 18 Aug 2008 14:23:05 GMT</pubDate><lastBuildDate>Mon, 18 Aug 2008 14:23:05 GMT</lastBuildDate><generator>Microsoft Spaces v1.1</generator><docs>http://www.rssboard.org/rss-specification</docs><ttl>60</ttl><cf:parentRSS>http://tangl99.spaces.live.com/blog/feed.rss</cf:parentRSS><live:type>blogcategory</live:type><live:identity><live:id>-508484562895931526</live:id><live:alias>tangl99</live:alias></live:identity><cf:listinfo><cf:group ns="http://schemas.microsoft.com/live/spaces/2006/rss" element="typelabel" label="Type" /><cf:group ns="http://schemas.microsoft.com/live/spaces/2006/rss" element="tag" label="Tag" /><cf:group element="category" label="Category" /><cf:sort element="pubDate" label="Date" data-type="date" default="true" /><cf:sort element="title" label="Title" data-type="string" /><cf:sort ns="http://purl.org/rss/1.0/modules/slash/" element="comments" label="Comments" data-type="number" /></cf:listinfo><item><title>SCU BSS讨论帖子</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1190.entry</link><description>&lt;p&gt;==============回复1================   &lt;p&gt;算法是计算机科学领域最重要的基石之一，但却受到了国内一些程序员的冷落。许多学生看到一些公司在招聘时要求的编程语言五花八门，就产生了一种误解，认为学计算机就是学各种编程语言，或者认为，学习最新的语言、技术、标准就是最好的铺路方法。其实，大家被这些公司误导了。编程语言虽然该学，但是学习计算机算法和理论更重要，因为计算机语言和开发平台日新月异，但万变不离其宗的是那些算法和理论，例如数据结构、算法、编译原理、计算机体系结构、关系型数据库原理等等。在“开复学生网”上，有位同学生动地把这些基础课程比拟为“内功”，把新的语言、技术、标准比拟为“外功”。整天赶时髦的人最后只懂得招式，没有功力，是不可能成为高手的。&lt;br&gt;算法与我&lt;br&gt;当我在1980年转入计算机科学系时，还没有多少人的专业方向是计算机科学。有许多其他系的人嘲笑我们说：“知道为什么只有你们系要加一个‘科学’，而没有‘物理科学系’或‘化学科学系’吗？因为人家是真的科学,不需要画蛇添足，而你们自己心虚，生怕不‘科学’，才这样欲盖弥彰。” 其实，这点他们彻底弄错了。真正学懂计算机的人（不只是“编程匠”）都对数学有相当的造诣，既能用科学家的严谨思维来求证，也能用工程师的务实手段来解决问题——而这种思维和手段的最佳演绎就是“算法”。&lt;br&gt;记得我读博时写的Othello对弈软件获得了世界冠军。当时，得第二名的人认为我是靠侥幸才打赢他，不服气地问我的程序平均每秒能搜索多少步棋，当他发现我的软件在搜索效率上比他快60多倍时，才彻底服输。为什么在同样的机器上，我可以多做60倍的工作呢？这是因为我用了一个最新的算法，能够把一个指数函数转换成四个近似的表，只要用常数时间就可得到近似的答案。在这个例子中，是否用对算法才是能否赢得世界冠军的关键。&lt;br&gt;还记得1988年贝尔实验室副总裁亲自来访问我的学校，目的就是为了想了解为什么他们的语音识别系统比我开发的慢几十倍，而且，在扩大至大词汇系统后，速度差异更有几百倍之多。他们虽然买了几台超级计算机，勉强让系统跑了起来，但这么贵的计算资源让他们的产品部门很反感，因为“昂贵”的技术是没有应用前景的。在与他们探讨的过程中，我惊讶地发现一个O(n*m)的动态规划（dynamic programming）居然被他们做成了O(n*n*m)。更惊讶的是，他们还为此发表了不少文章，甚至为自己的算法起了一个很特别的名字，并将算法提名到一个科学会议里，希望能得到大奖。当时，贝尔实验室的研究员当然绝顶聪明，但他们全都是学数学、物理或电机出身，从未学过计算机科学或算法，才犯了这么基本的错误。我想那些人以后再也不会嘲笑学计算机科学的人了吧!&lt;br&gt;网络时代的算法&lt;br&gt;有人也许会说：“今天计算机这么快，算法还重要吗?”其实永远不会有太快的计算机，因为我们总会想出新的应用。虽然在摩尔定律的作用下，计算机的计算能力每年都在飞快增长，价格也在不断下降。可我们不要忘记，需要处理的信息量更是呈指数级的增长。现在每人每天都会创造出大量数据（照片，视频，语音，文本等等）。日益先进的记录和存储手段使我们每个人的信息量都在爆炸式的增长。互联网的信息流量和日志容量也在飞快增长。在科学研究方面，随着研究手段的进步，数据量更是达到了前所未有的程度。无论是三维图形、海量数据处理、机器学习、语音识别，都需要极大的计算量。在网络时代，越来越多的挑战需要靠卓越的算法来解决。&lt;br&gt;再举另一个网络时代的例子。在互联网和手机搜索上，如果要找附近的咖啡店，那么搜索引擎该怎么处理这个请求呢?&lt;br&gt;最简单的办法就是把整个城市的咖啡馆都找出来,然后计算出它们的所在位置与你之间的距离,再进行排序,然后返回最近的结果。但该如何计算距离呢？图论里有不少算法可以解决这个问题。&lt;br&gt;这么做也许是最直观的，但绝对不是最迅速的。如果一个城市只有为数不多的咖啡馆,那这么做应该没什么问题,反正计算量不大。但如果一个城市里有很多咖啡馆,又有很多用户都需要类似的搜索,那么服务器所承受的压力就大多了。在这种情况下,我们该怎样优化算法呢?&lt;br&gt;首先，我们可以把整个城市的咖啡馆做一次“预处理”。比如，把一个城市分成若干个“格子(grid)”,然后根据用户所在的位置把他放到某一个格子里，只对格子里的咖啡馆进行距离排序。&lt;br&gt;问题又来了，如果格子大小一样，那么绝大多数结果都可能出现在市中心的一个格子里，而郊区的格子里只有极少的结果。在这种情况下，我们应该把市中心多分出几个格子。更进一步，格子应该是一个“树结构”，最顶层是一个大格——整个城市，然后逐层下降，格子越来越小，这样有利于用户进行精确搜索——如果在最底层的格子里搜索结果不多，用户可以逐级上升，放大搜索范围。&lt;br&gt;上述算法对咖啡馆的例子很实用，但是它具有通用性吗？答案是否定的。把咖啡馆抽象一下，它是一个“点”，如果要搜索一个“面”该怎么办呢？比如，用户想去一个水库玩，而一个水库有好几个入口，那么哪一个离用户最近呢？这个时候，上述“树结构”就要改成“r-tree”,因为树中间的每一个节点都是一个范围，一个有边界的范围（参考：http://www.cs.umd.edu/~hjs/rtrees/index.html）。&lt;br&gt;通过这个小例子，我们看到，应用程序的要求千变万化，很多时候需要把一个复杂的问题分解成若干简单的小问题，然后再选用合适的算法和数据结构。&lt;br&gt;并行算法：Google的核心优势&lt;br&gt;上面的例子在Google里就要算是小case了！每天Google的网站要处理十亿个以上的搜索，GMail要储存几千万用户的2G邮箱，Google Earth要让数十万用户同时在整个地球上遨游，并将合适的图片经过互联网提交给每个用户。如果没有好的算法，这些应用都无法成为现实。&lt;br&gt;在这些的应用中，哪怕是最基本的问题都会给传统的计算带来很大的挑战。例如，每天都有十亿以上的用户访问Google的网站，使用Google的服务，也产生很多很多的日志（Log）。因为Log每分每秒都在飞速增加，我们必须有聪明的办法来进行处理。我曾经在面试中问过关于如何对log进行一些分析处理的问题，有很多面试者的回答虽然在逻辑上正确，但在实际应用中是几乎不可行的。按照他们的算法，即便用上几万台机器，我们的处理速度都跟不上数据产生的速度。&lt;br&gt;那么Google是如何解决这些问题的呢？&lt;br&gt;首先，在网络时代，就算有最好的算法，也要能在并行计算的环境下执行。在Google的数据中心，我们使用的是超大的并行计算机。但传统的并行算法运行时，效率会在增加机器数量后迅速降低，也就是说，十台机器如果有五倍的效果，增加到一千台时也许就只有几十倍的效果。这种事倍功半的代价是没有哪家公司可以负担得起的。而且，在许多并行算法中，只要一个结点犯错误，所有计算都会前功尽弃。&lt;br&gt;那么Google是如何开发出既有效率又能容错的并行计算的呢？&lt;br&gt;Google最资深的计算机科学家Jeff Dean认识到， Google 所需的绝大部分数据处理都可以归结为一个简单的并行算法：Map and Reduce（http://labs.google.com/papers/mapreduce.html）。 这个算法能够在很多种计算中达到相当高的效率，而且是可扩展的（也就是说，一千台机器就算不能达到一千倍的效果，至少也可以达到几百倍的效果）。Map and Reduce的另外一大特色是它可以利用大批廉价的机器组成功能强大的server farm。最后，它的容错性能异常出色，就算一个server farm里面的机器down掉一半，整个farm依然能够运行。正是因为这个天才的认识,才有了Map and Reduce算法。借助该算法，Google几乎能无限地增加计算量，与日新月异的互联网应用一同成长。&lt;br&gt;算法并不局限于计算机和网络&lt;br&gt;举一个计算机领域外的例子：在高能物理研究方面，很多实验每秒钟都产生几个TB的数据量。但因为处理能力和存储能力的不足，科学家不得不把绝大部分未经处理的数据丢弃掉。可大家要知道，新元素的信息很有可能就藏在我们来不及处理的数据里面。同样的，在其他任何领域里，算法都可以改变人类的生活。例如人类基因的研究，就可能因为算法而发明新的医疗方式。在国家安全领域，有效的算法可能避免下一个911的发生。在气象方面，算法可以更好地预测未来天灾的发生，以拯救生命。&lt;br&gt;所以，如果你把计算机的发展放到应用和数据飞速增长的大环境下，你一定会发现，算法的重要性不是在日益减小，而是在日益加强。&lt;br&gt;给程序员的七个建议&lt;br&gt;（1）练内功。不要只花功夫学习各种流行的编程语言和工具，以及某些公司招聘广告上要求的科目。要把数据结构、算法、数据库、操作系统原理、计算机体系结构、计算机网络，离散数学等基础课程学好。大家不妨试试高德纳所著The Art of Computer Programming里的题目，如果你能够解决其中的大部分题目，就说明你在算法方面有一定的功力了。&lt;br&gt;（2）多实战。通过编程的实战积累经验、巩固知识。很多中国大学毕业生缺乏编程和调试经验；学习C语言，考试过关就算学会了；课题项目中，只要程序能够编译，运行，并且输入输出满足要求就算了事。这些做法是不行的。写程序的时候，大家必须多想想如何把程序写得更加精炼、高效、高质量。建议大家争取在大学四年中积累编写十万行代码的经验。我们必须明白的是：好程序员是写出来的，不是学出来的。&lt;br&gt;（3）求实干。不要轻视任何实际工作，比如一些看似简单的编码或测试。要不懈追求对细节一丝不苟的实干作风与敬业精神。我发现不少程序员对于知识的掌握很肤浅，不求甚解，没有好奇心，不会刨根问底。比如，学会了C++，是否了解一个对象在编译后，在汇编代码中是如何被初始化的？这个对象的各个成员在内存中是如何存放的？当一个成员函数被调用时，编译器在汇编代码中加入了哪些额外的动作？虚函数的调用是如何实现的? 这些东西恐怕在编程语言或编译原理中都没有详细提到，只有通过踏实的实干才能真正掌握。&lt;br&gt;（4）重视数学学习。数学是思维的体操，数学无处不在。学计算机至少要学习离散数学、概率论、布尔代数、集合论和数理逻辑。这些知识并不难，但是对你未来的工作帮助会很大。 尤其当你对一些“数学密集型”的领域如视频、图像处理等有兴趣时，这些知识将成为你手中的利器。&lt;br&gt;（5）培养团队精神，学会与人合作。今天的软件工程早已经不是一个人可以单独操作的，而必须靠团队合作才能成功。不懂得合作的人是不能成大器的。大家要多去寻找可以与人一起做项目的机会。&lt;br&gt;（6）激励创新意识，培养好奇心，不要死记硬背。没有掌握某种算法技术的根本原理，就不会有应变和创新的能力。想成为一位好程序员（其实从事任何一个行业都是如此），重要的是要养成钻研，好奇，创新，动手，合作的优秀习惯，不满足于填鸭，不满足于考试交差，不满足于表象。这不是学几门课能够一蹴而就的。&lt;br&gt;（7）有策略地“打工”。在不影响学业的前提下，寻找真正有意义的暑期工作或兼职。去找一个重视技术的公司，在一个好的“老板”指导下完成真正会被用户使用的程序。不要急于去一个要你做“头”而独挡一面的地方，因为向别人学习才是你的目的。找工作也是一样，不要只看待遇和职衔，要挑一个你能够学习的环境，一个愿意培养员工的企业，一个重视你的专业的公司。最后，还要挑一个好老板。&lt;br&gt;希望大家都能把握机会，养成好的学习习惯，把算法学精学透；希望大家都能有一个美好的未来！&lt;br&gt;所以大家加油哈～～～ &lt;p&gt;=====================回复2====================== &lt;p&gt;其实ACM比赛里面的谈的算法，和现代计算理论的发展还有一定方向的距离。&lt;br&gt;举个例子，最近ICDE上提出的全世界“十大机器学习”算法，在ACM比赛里面看来，几乎都是连选拔赛都进不了的easy case。不少算法，基本上就是一个求和，相减，相除就可以完成的。为什么呢？&lt;br&gt;现在计算机学界的发展主流，早就不是细节计算过程的优化，而是针对新的，更好的计算模型，数学模型的提出和改进。数学模型，计算模型的研究，虽然实现的计算过程很简单，但是代表的是一种思想，而不仅仅只是仅仅看成计算机内存单元之间的运算。比如一个朴素贝叶斯的提出，大家都知道其算法实现是多么简单吧，一个刚学C语言的同学都会写，难道能说朴素贝叶斯是一个普通大一学生就能提出的吗？楼主说的别人实验室里面一个O(n*m)做成O(n*n*m)这样的情况其实很多了，并不奇怪。这样就取笑别人，只能让自己捡了芝麻丢了西瓜。我们要学习的，应该是别人做的东西真正的精髓。多学习别人精髓方面做得出色的地方，而不是具体实现的失误。实际上，很多顶尖的学术文章，在讲解算法的时候，往往都是以最简单，最直白的算法写出来，并不是别人不知道怎么优化，而是别人关键要把整个计算的过程阐述清楚。具体实现的时候，你可以运用诸多的动态优化，各种树，各种表，去改进它的时间复杂度。这个在别人研究者面前，不是关心的主要问题。&lt;br&gt;大家都知道，在计算机领域，最一流的工作都是提出新的问题，二流的工作是提出一个计算模型去解决这个问题，三流的工作才是改进已有的解决办法。中国人现在做科研的时候，往往都是在别人提出一种新的计算模型下，然后运用各种技巧改进别人实现过程。当然，这个工作也是值得称赞的。但是，这只是一个三流工作。为什么中国不能出大师呢？&lt;br&gt;不要觉得大学开的基础课程都是外功，只有算法才是内功。大学选择的这些课程，都是全世界最顶尖的计算机专家共同协商的，全世界每个计算机本科都上这些课程。如果学好基本算法，就可以走遍天下都不怕的话，那为什么大学里面不主要就开算法课程？难道这些顶尖的计算机专家都是故意让我们走弯路？其实不然，大学里面，关系数据库，计算机网络等，核心的内容在什么地方？绝对不只是算法的实现，而是解决模型的提出。比如说，存储查询的关系模型，网络上的通讯，路由等模型。先提出了模型，才有实现的算法。这个模型不是上上算法课程就能学得会的。我知道ACM算法课里面也是 先讲解很多题目的解决模型，然后讲算法的。实际上解决任何一个问题都是如此。问题在于，大学的这些基础课程讲解的，是现实中用得最广泛的，相对有一套完成的理论体系的。这些课程涉及面很广，任何一个领域都是足够让你消耗一辈子的精力去学习和研究。这些领域提出的各种问题，各种解决模型，不是ACM课上那种光讲讲例题就能完了的，它们在现实应用广泛，问题很多，需要很深入地系统学习和研究，而不只是做几道编程题目就算是学会的。&lt;br&gt;随便八卦一下，为什么ACM队伍里面那么多牛人，最后没几个选择从事到计算机的研究工作去呢？为什么大家都去做工程了？&lt;br&gt;【 在 jjjscuedu (jjjscuedu) 的大作中提到: 】&lt;br&gt;:  算法是计算机科学领域最重要的基石之一，但却受到了国内一些程序员的冷落。许多学生看到一些公司在招聘时要求的编程语言五花八门，就产生了一种误解，认为学计算机就是学各种编程语言，或者认为，学习最新的语言、技术、标准就是最好的铺路方法。其实，大家被这些公司误导了。编程语言虽 &lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+SCU+BSS%e8%ae%a8%e8%ae%ba%e5%b8%96%e5%ad%90&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1190.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1190.entry</guid><pubDate>Thu, 19 Jun 2008 04:50:22 GMT</pubDate><slash:comments>1</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!1190/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1190.entry#comment</wfw:comment><dcterms:modified>2008-06-19T04:50:41Z</dcterms:modified></item><item><title>C Low-Level I/O functions</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1128.entry</link><description>&lt;p&gt; 以前在看别人的很多代码时候，经常看到低级的C语言文件I/O函数。不过自己却从来还没有用到过低级的I/O函数。在纯C里面就f开头的stream用得多。这次要做SIDB在底层实现数据库存储文件，就得必须依靠低级文件I/O才能做了。因为低级文件I/O提供了某些功能，而高级文件I/O函数中没有。 &lt;p&gt;  这是我在网上找到为什么要使用低级IO的一些原因: &lt;p&gt;摘自: &lt;a title="http://www.linuxtopia.org/online_books/programming_books/gnu_libc_guide/Low_002dLevel-I_002fO.html" href="http://www.linuxtopia.org/online_books/programming_books/gnu_libc_guide/Low_002dLevel-I_002fO.html"&gt;http://www.linuxtopia.org/online_books/programming_books/gnu_libc_guide/Low_002dLevel-I_002fO.html&lt;/a&gt; &lt;table cellspacing=0 cellpadding=2 width=548 border=1&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign=top width=546&gt; &lt;ul&gt; &lt;li&gt;&lt;em&gt;For reading binary files in large chunks. &lt;/em&gt; &lt;li&gt;&lt;em&gt;For reading an entire file into core before parsing it. &lt;/em&gt; &lt;li&gt;&lt;em&gt;To perform operations other than data transfer, which can only be done with a descriptor. (You can use &lt;code&gt;fileno&lt;/code&gt; to get the descriptor corresponding to a stream.) &lt;/em&gt; &lt;li&gt;&lt;em&gt;To pass descriptors to a child process. (The child can create its own stream to use a descriptor that it inherits, but cannot inherit a stream directly.)&lt;/em&gt; &lt;/ul&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;  &lt;p&gt;下面是我总结一些文件必须使用Low-Level的I/O函数的原因: &lt;table cellspacing=0 cellpadding=2 width=548 border=1&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign=top width=546&gt;1. 低级IO没有提供buffer,而我们使用f开头以及C++等很多文件操作，都是带缓冲的。&lt;br&gt;2. 低级IO在功能上更加强大。据我所知，诸如_filelength和_chsize，获取文件的长度和改变文件的长度，这两个函数，在高级文件IO中是找不到的。&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;关于Low-Level的文件I/O资料，可以在下面网址找到：&lt;br&gt;&lt;a title="http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_8.html" href="http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_8.html"&gt;http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_8.html&lt;/a&gt;&lt;br&gt;&lt;a title="http://www.gnu.org/software/libc/manual/html_node/Low_002dLevel-I_002fO.html" href="http://www.gnu.org/software/libc/manual/html_node/Low_002dLevel-I_002fO.html"&gt;http://www.gnu.org/software/libc/manual/html_node/Low_002dLevel-I_002fO.html&lt;/a&gt;&lt;br&gt;&lt;a title="http://www.informit.com/guides/content.aspx?g=cplusplus&amp;amp;seqNum=208" href="http://www.informit.com/guides/content.aspx?g=cplusplus&amp;amp;seqNum=208"&gt;http://www.informit.com/guides/content.aspx?g=cplusplus&amp;amp;seqNum=208&lt;/a&gt;&lt;br&gt;&lt;a title="http://www.thinkage.ca/english/gcos/expl/c/lib/open.html" href="http://www.thinkage.ca/english/gcos/expl/c/lib/open.html"&gt;http://www.thinkage.ca/english/gcos/expl/c/lib/open.html&lt;/a&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+C+Low-Level+I%2fO+functions&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1128.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1128.entry</guid><pubDate>Mon, 24 Dec 2007 06:19:17 GMT</pubDate><slash:comments>4</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!1128/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1128.entry#comment</wfw:comment><dcterms:modified>2007-12-24T06:19:17Z</dcterms:modified></item><item><title>关于Windows下C++开发的64位和32位通讯的问题</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1127.entry</link><description>&lt;p&gt;  现在64位的系统已经十分普及了。在MSRA实习的时候，几乎所有的Server都是64位的Windows Server，而我自己开发使用的机器还是普通的32位系统。在处理网络通讯的时候，经常需要把整数类型的数据转换成二进制流传输，那么就会遇到32位机器和64位机器之间通讯的情况。 &lt;p&gt;  C++默认的int变量，都是根据具体编译环境来定，如果是32位下编译，就是4个字节宽度，如果是64位机器下就是8个字节宽度。那么在通讯的时候，我们需要强制同意按照一种整数宽度来读和写。Windows SDK里面已经提供了一个自定义的数据类型来实现统一宽度。在windef.h里面，INT, UINT,DWORD等通过typedef自定义的数据类型，无论在32还是64位下，都是统一的宽度。下面是我从MSDN上找到的比较官方的说明： &lt;p&gt;摘自:&lt;a title="http://www.microsoft.com/china/MSDN/library/Windev/64bit/MW6TWPchapter5.mspx?mfr=true" href="http://www.microsoft.com/china/MSDN/library/Windev/64bit/MW6TWPchapter5.mspx?mfr=true"&gt;http://www.microsoft.com/china/MSDN/library/Windev/64bit/MW6TWPchapter5.mspx?mfr=true&lt;/a&gt; &lt;table cellspacing=0 cellpadding=2 width=767 border=1&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign=top width=765&gt; &lt;h4&gt;&lt;em&gt;5.3 编码问题&lt;/em&gt;&lt;/h4&gt; &lt;p&gt;&lt;em&gt;将 C/C++ 应用程序迁移到 64 位时，遇到的大部分编码问题可分为三类：指针转换、指针运算和对齐。在处理内联汇编程序，使用修改后的五个 API 调用之一，以及尝试跨 32/64 位边界通信时，可能会出现其他问题。 &lt;/em&gt; &lt;p&gt;&lt;em&gt;由于数据类型 Int 和 Long 的长度保持不变（仍为 32 位），因此需要修改的代码数量非常少。通常，所涉及的代码行数应该不到总代码基的 1％。这与 Unix 不同，其中 Long 要迁移到 64 位。 &lt;/em&gt; &lt;p&gt;&lt;em&gt;开发人员必须小心处理变量的对齐。未对齐对性能的影响非常严重。对于 x64，有一些性能影响，但是在 Itanium 系统上，问题更严重；异常将传播到应用程序层并会导致应用程序崩溃。 &lt;/em&gt; &lt;p&gt;&lt;em&gt;开发人员可以使用 –Wp64 编译器开关来要求编译器显示可能的移植性问题。这将使开发人员能够注意到绝大多数的移植问题。该标志在 32 位模式下同样可用。 &lt;/em&gt; &lt;p&gt;&lt;em&gt;&lt;b&gt;5.3.1&lt;/b&gt;&lt;b&gt;类型大小&lt;/b&gt; &lt;/em&gt; &lt;p&gt;&lt;em&gt;从 32 位迁移到 64 位时，增长的主要类型是指针和派生数据类型，如句柄。在 Windows 64 位中，目前的指针和派生类型是 64 位 long 类型。大小增加的其他一些类型还有：WPARAM、LPARAM、LRESULT 和 SIZE_T。其中一个原因是，它们作为参数使用，并且某些函数将指针作为参数使用。 &lt;/em&gt; &lt;p&gt;&lt;em&gt;从“int”和“long”派生出的所有类型的大小仍然是 32 位，其中包括 DWORD、UINT 和 ULONG。小于 32 位的类型保留它们当前的大小。一个示例就是“short”数据类型，它仍然保留为 16 位的带符号整数。 &lt;/em&gt; &lt;p&gt;&lt;em&gt;正如前面提到的那样，Win32 API 保持不变。所进行的更改对应于五个替代函数；其中，四个由一个多态版本取代，一个用于平面滚动条： &lt;/em&gt; &lt;p&gt;&lt;em&gt;GetClassLongPtr() &lt;/em&gt; &lt;p&gt;&lt;em&gt;GetWindowLongPtr() &lt;/em&gt; &lt;p&gt;&lt;em&gt;SetClassLongPtr() &lt;/em&gt; &lt;p&gt;&lt;em&gt;SetWindowLongPtr() &lt;/em&gt; &lt;p&gt;&lt;em&gt;这些函数的名称已经更改。此外，这些函数已经调整为使用多态数据类型（如 UINT_PTR），并使用所有更新的常量。&lt;/em&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+%e5%85%b3%e4%ba%8eWindows%e4%b8%8bC%2b%2b%e5%bc%80%e5%8f%91%e7%9a%8464%e4%bd%8d%e5%92%8c32%e4%bd%8d%e9%80%9a%e8%ae%af%e7%9a%84%e9%97%ae%e9%a2%98&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1127.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1127.entry</guid><pubDate>Sun, 23 Dec 2007 14:07:59 GMT</pubDate><slash:comments>1</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!1127/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!1127.entry#comment</wfw:comment><dcterms:modified>2007-12-23T14:11:26Z</dcterms:modified></item><item><title>Microsoft Visual C++ Default Keybindings</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!926.entry</link><description>&lt;p&gt;  Microsoft provides the document about the keybindings of Visual C++, which may be useful for the coders working in the MS Visual C++. &lt;p&gt;  This is the picture of the whole document: &lt;p&gt;&lt;a href="http://byfiles.storage.msn.com/y1pvIUwXXcLuQJxe0qcp3OU_k70xb6uAcYIf0jgDF1-VBG2E1DGnfTyJiWB3MFual7o"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px" height=456 alt=image src="http://byfiles.storage.msn.com/y1pvIUwXXcLuQKfdxHZl-6jRt5pfJ82NqGC3tinL0CcUeZXsXbIqXuBvGOb2j4zD_pF" width=604 border=0&gt;&lt;/a&gt;  &lt;p&gt;  &lt;p&gt;Here is the pdf download link: &lt;a title="http://www.microsoft.com/downloads/details.aspx?FamilyID=bccf84f4-4136-48b2-b4ec-83eaa484da20&amp;amp;DisplayLang=en" href="http://www.microsoft.com/downloads/details.aspx?FamilyID=bccf84f4-4136-48b2-b4ec-83eaa484da20&amp;amp;DisplayLang=en"&gt;http://www.microsoft.com/downloads/details.aspx?FamilyID=bccf84f4-4136-48b2-b4ec-83eaa484da20&amp;amp;DisplayLang=en&lt;/a&gt;&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+Microsoft+Visual+C%2b%2b+Default+Keybindings&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!926.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!926.entry</guid><pubDate>Wed, 11 Jul 2007 04:41:43 GMT</pubDate><slash:comments>0</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!926/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!926.entry#comment</wfw:comment><dcterms:modified>2007-07-11T04:41:43Z</dcterms:modified></item><item><title>通过MSXML来读写XML</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!912.entry</link><description>&lt;p&gt;以往在C#或者Java里面，都有很好的DOM来支持读写XML文档，在C++里面也有相应的不少。而MSXML是Windows下最常用的一个组件，以COM组件单独发布出来称为一个WINDOWS组件的。 &lt;p&gt;首先需要通过#import &amp;quot;msxml.dll&amp;quot;连链接MSXML的dll &lt;p&gt;然后通过CoCreateInstance来创建COM接口: &lt;p&gt;CComPtr&amp;lt;IXMLDOMDocument&amp;gt; pDoc;&lt;br&gt;CComPtr&amp;lt;IXMLDOMElement&amp;gt; elementRoot;&lt;br&gt;CComPtr&amp;lt;IXMLDOMNode&amp;gt; pNode;&lt;br&gt;CComPtr&amp;lt;IXMLDOMDocument&amp;gt; pElement;&lt;br&gt;VARIANT_BOOL isSuccessful;&lt;br&gt;VARIANT varValue;&lt;br&gt;&lt;br&gt;hr = ::CoCreateInstance(CLSID_DOMDocument, 0, CLSCTX_INPROC, IID_IXMLDOMDocument, (void **)(&amp;amp;pDoc));&lt;br&gt;if(FAILD(hr))&lt;br&gt;{&lt;br&gt;   _ASSERT(false):&lt;br&gt;   return hr;&lt;br&gt;}&lt;br&gt;hr = pRequestDoc-&amp;gt;loadXML(BSTR(wszXml), &amp;amp;isSuccessful);&lt;br&gt;if(FAILD(hr) || !isSuccessful)&lt;br&gt;{&lt;br&gt;   _ASSERT(false):&lt;br&gt;   return hr;&lt;br&gt;} &lt;p&gt;hr = elementRoot-&amp;gt;selectSingleNode(L&amp;quot;/testnode&amp;quot;, &amp;amp;pNode);&lt;br&gt;if(FAILD(hr) || !isSuccessful)&lt;br&gt;{&lt;br&gt;   _ASSERT(false):&lt;br&gt;   return hr;&lt;br&gt;}&lt;br&gt;&lt;br&gt;pElement = pNode; &lt;p&gt;hr = pRequestElement-&amp;gt;getAttribute(L&amp;quot;type&amp;quot;, &amp;amp;varValue); &lt;p&gt;if (varValue.vt == VT_BSTR)&lt;br&gt;{&lt;br&gt;      std::wstring wszTypeName = varValue.bstrVal;&lt;br&gt;      std::wcout&amp;lt;&amp;lt;wszTypeName&amp;lt;&amp;lt;endl;&lt;br&gt;} &lt;p&gt;有趣的是这个VARIANT这个结构体。 &lt;p&gt;typedef /* [wire_marshal] */ struct tagVARIANT VARIANT;  &lt;p&gt;struct tagVARIANT&lt;br&gt;    {&lt;br&gt;    union &lt;br&gt;        {&lt;br&gt;        struct __tagVARIANT&lt;br&gt;            {&lt;br&gt;            VARTYPE vt;&lt;br&gt;            WORD wReserved1;&lt;br&gt;            WORD wReserved2;&lt;br&gt;            WORD wReserved3;&lt;br&gt;            union &lt;br&gt;                {&lt;br&gt;                LONGLONG llVal;&lt;br&gt;                LONG lVal;&lt;br&gt;                BYTE bVal;&lt;br&gt;                SHORT iVal;&lt;br&gt;                FLOAT fltVal;&lt;br&gt;                DOUBLE dblVal;&lt;br&gt;                VARIANT_BOOL boolVal;&lt;br&gt;                _VARIANT_BOOL bool;&lt;br&gt;                SCODE scode;&lt;br&gt;                CY cyVal;&lt;br&gt;                DATE date;&lt;br&gt;                BSTR bstrVal;&lt;br&gt;                IUnknown *punkVal;&lt;br&gt;                IDispatch *pdispVal;&lt;br&gt;                SAFEARRAY *parray;&lt;br&gt;                BYTE *pbVal;&lt;br&gt;                SHORT *piVal;&lt;br&gt;                LONG *plVal;&lt;br&gt;                LONGLONG *pllVal;&lt;br&gt;                FLOAT *pfltVal;&lt;br&gt;                DOUBLE *pdblVal;&lt;br&gt;                VARIANT_BOOL *pboolVal;&lt;br&gt;                _VARIANT_BOOL *pbool;&lt;br&gt;                SCODE *pscode;&lt;br&gt;                CY *pcyVal;&lt;br&gt;                DATE *pdate;&lt;br&gt;                BSTR *pbstrVal;&lt;br&gt;                IUnknown **ppunkVal;&lt;br&gt;                IDispatch **ppdispVal;&lt;br&gt;                SAFEARRAY **pparray;&lt;br&gt;                VARIANT *pvarVal;&lt;br&gt;                PVOID byref;&lt;br&gt;                CHAR cVal;&lt;br&gt;                USHORT uiVal;&lt;br&gt;                ULONG ulVal;&lt;br&gt;                ULONGLONG ullVal;&lt;br&gt;                INT intVal;&lt;br&gt;                UINT uintVal;&lt;br&gt;                DECIMAL *pdecVal;&lt;br&gt;                CHAR *pcVal;&lt;br&gt;                USHORT *puiVal;&lt;br&gt;                ULONG *pulVal;&lt;br&gt;                ULONGLONG *pullVal;&lt;br&gt;                INT *pintVal;&lt;br&gt;                UINT *puintVal;&lt;br&gt;                struct __tagBRECORD&lt;br&gt;                    {&lt;br&gt;                    PVOID pvRecord;&lt;br&gt;                    IRecordInfo *pRecInfo;&lt;br&gt;                    }     __VARIANT_NAME_4;&lt;br&gt;                }     __VARIANT_NAME_3;&lt;br&gt;            }     __VARIANT_NAME_2;&lt;br&gt;        DECIMAL decVal;&lt;br&gt;        }     __VARIANT_NAME_1;&lt;br&gt;    } ; &lt;p&gt;它把各种数据类型都封装到一个union，然后再加上vt一个来确定数据类型的type，还有wReserved1-wReserved3三个保留字段。这个结构体虽然简单，但是却在很多其他地方，特别是未知数据类型的变量参数传递的时候有很重要的方便作用。&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+%e9%80%9a%e8%bf%87MSXML%e6%9d%a5%e8%af%bb%e5%86%99XML&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!912.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!912.entry</guid><pubDate>Tue, 03 Jul 2007 02:51:28 GMT</pubDate><slash:comments>2</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!912/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!912.entry#comment</wfw:comment><dcterms:modified>2007-07-03T02:51:28Z</dcterms:modified></item><item><title>windbg the easy way(part2)</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!899.entry</link><description>&lt;h6&gt;&lt;a&gt;Saving dumps &lt;/a&gt;&lt;/h6&gt; &lt;p&gt;When debugging a problem that is not easy to reproduce, I sometimes want to make a snapshot of the application's state (memory contents, the list of open handles, and so on) and save it in a file for further analysis. It can be useful when, for example, I suspect that the current state can contain the key to the problem I am trying to solve, but want to continue running the application to see how the situation develops. Sometimes I make a series of snapshots, one after another, so that I could compare them later and see how some data structures change while the application is running. And I always create a snapshot when I have finally managed to reproduce the problem, to make sure that I don't lose valueable information if, for example, I close the debugging session by mistake. Probably, it is not difficult to guess that when I say “snapshot” I actually mean “minidump”, because minidumps proved to be very convenient for saving the application state at any moment of time.  &lt;p&gt;Here is the command line that can be used to create a minidump: &lt;pre&gt;  cdb -pv -pn myapp.exe -c &amp;quot;.dump /m c:\myapp.dmp;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Let's take a closer look at .dump command. In the example above, this command receives only one option (/m) and is followed by the name of the minidump file. /m option is used to specify what kinds of information should be included into the minidump. The most important (in my opinion) variants of /m option are listed in the following table: 
&lt;p&gt;Option&lt;br&gt;Description&lt;br&gt;Example
&lt;p&gt;/m&lt;br&gt;This option is used by default. It creates a standard minidump, equivalent to &lt;a href="http://www.debuginfo.com/effminidumps.html#minidumpnormal"&gt;MiniDumpNormal&lt;/a&gt; minidump type. The resulting minidump is usually very small, and therefore this option is useful if you want to transfer the minidump over a slow network. But unfortunately, small size of the minidump also means that in most cases it does not contain enough information for serious analysis (you can find more information about minidump contents in this &lt;a href="http://www.debuginfo.com/effminidumps.html"&gt;article&lt;/a&gt;) &lt;br&gt;.dump /m c:\myapp.dmp
&lt;p&gt;/ma&lt;br&gt;Minidump with all possible options (complete memory contents, handles, unloaded modules, etc.), and the resulting minidump can be huge. This option is the best for local debugging, if disk space is not limited. &lt;br&gt;.dump /ma c:\myapp.dmp
&lt;p&gt;/mFhutwd&lt;br&gt;Minidump with data sections, non-shared read/write memory pages and other useful information. This option can be used if you want to collect as much information as possible, but still need to keep the minidump relatively small (and compressible). &lt;br&gt;.dump /mFhutwd c:\myapp.dmp
&lt;p&gt;The following command creates a minidump that includes all possible kinds of information: &lt;pre&gt;  cdb -pv -pn myapp.exe -c &amp;quot;.dump /ma c:\myapp.dmp;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;What if we want to create a new minidump and overwrite the existing one? By default, .dump command does not allow to do it – it complains that the file with the given name already exists. To change the default behavior and overwrite the existing minidump file, we can use /o option: &lt;pre&gt;  cdb -pv -pn myapp.exe -c &amp;quot;.dump /ma /o c:\myapp.dmp;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;If we want to create a series of minidumps, one after another, it can be handy to name the minidump files so that the names reflect the time when the minidumps were created. Good news are that .dump command can do it automatically, if we specify /u option. For example, the following command could produce a minidump called myapp_02CC_&lt;b&gt;2006-01-28_04-11-18-171&lt;/b&gt;_0158.dmp (0158 is the process id): &lt;pre&gt;  cdb -pv -pn myapp.exe -c &amp;quot;.dump /m /u c:\myapp.dmp;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;.dump command also supports other interesting options (you can find them in the documentation). 
&lt;p&gt;If you want to create a minidump of a process that is running under Visual Studio debugger, I would recommend to temporarily disable all breakpoints in Visual Studio before creating the dump. If breakpoints are not disabled, the minidump will contain breakpoint instructions (int 3) inserted by Visual Studio debugger into the code of the target process. 
&lt;h6&gt;&lt;a&gt;Crash dump analysis &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;CDB can also be used to automate crash dump analysis. The automation is possible because we usually have to perform the same set of operations when we start analysing a crash dump. What operations? It depends on the kind of the crash dump. I would separate all crash dumps into two main categories: 
&lt;ul&gt;
&lt;li&gt;crash dumps with exception information 
&lt;li&gt;crash dumps without exception information &lt;/ul&gt;
&lt;p&gt;Crash dumps with exception information are usually created when the application raises an unhandled exception and invokes a just-in-time debugger (Dr. Watson, &lt;a href="http://www.debuginfo.com/ntsdwatson.html"&gt;NTSD&lt;/a&gt;, or other) or creates a minidump in the &lt;a href="http://msdn.microsoft.com/library/en-us/debug/base/setunhandledexceptionfilter.asp"&gt;custom filter for unhandled exceptions&lt;/a&gt;. Exception information is written into the crash dump so that we could determine the type of the exception and the place in the code where it occurred. Crash dumps without exception information are usually created manually, when we want to create a snapshot of the process for further analysis (for example, with the help of techniques described in the &lt;a href="http://www.debuginfo.com/#savedumps"&gt;previous chapter&lt;/a&gt; of this article). 
&lt;p&gt;When we start debugging a crash dump with exception information, we usually want to know the following: 
&lt;ul&gt;
&lt;li&gt;the place in the code where the exception occurred (address, source file and line number) 
&lt;li&gt;call stack at the moment when the exception was raised 
&lt;li&gt;values of function parameters and local variables, for some or all functions on the call stack &lt;/ul&gt;
&lt;p&gt;WinDbg and CDB support a very useful command for crash dump debugging - !analyze. This command analyzes exception information in the crash dump, determines the place where the exception occurred, the call stack, and displays detailed report. Here is how to use this command: &lt;pre&gt;  cdb -z c:\myapp.dmp -logo out.txt -lines -c &amp;quot;!analyze -v;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;(-v option asks !analyze to display verbose output) 
&lt;p&gt;&lt;a href="http://www.debuginfo.com/examples/src/CrashDemo.cpp"&gt;CrashDemo.cpp&lt;/a&gt; sample demonstrates how to use a custom filter to catch unhandled exceptions and create minidumps. If you compile it and run, and then use the abovementioned CDB command to analyze the resulting minidump, you will get an output similar to the following: &lt;pre&gt;0:001&amp;gt; !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

FAULTING_IP: 
CrashDemo!TestFunc+2e [c:\tests\crashdemo\crashdemo.cpp @ 124]
004309de c70000000000     mov     dword ptr [eax],0x0

EXCEPTION_RECORD:  ffffffff -- (.exr ffffffffffffffff)
.exr ffffffffffffffff
&lt;b&gt;ExceptionAddress: 004309de&lt;/b&gt; (CrashDemo!TestFunc+0x0000002e)
&lt;b&gt;ExceptionCode: c0000005 (Access violation)&lt;/b&gt;
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 00000000
Attempt to write to address 00000000

DEFAULT_BUCKET_ID:  APPLICATION_FAULT

PROCESS_NAME:  CrashDemo.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at &amp;quot;0x%08lx&amp;quot; referenced memory 
  at &amp;quot;0x%08lx&amp;quot;. The memory could not be &amp;quot;%s&amp;quot;.

WRITE_ADDRESS:  00000000 

BUGCHECK_STR:  ACCESS_VIOLATION

LAST_CONTROL_TRANSFER:  from 0043096e to 004309de

&lt;b&gt;STACK_TEXT:&lt;/b&gt;
006afe88 0043096e 00000000 00354130 00350001 CrashDemo!TestFunc+0x2e 
  [c:\tests\crashdemo\crashdemo.cpp @ 124]
006aff6c 00430f31 00000000 52319518 00354130 CrashDemo!WorkerThread+0x5e 
  [c:\tests\crashdemo\crashdemo.cpp @ 115]
006affa8 00430ea2 00000000 006affec 7c80b50b CrashDemo!_callthreadstartex+0x51 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
006affb4 7c80b50b 00355188 00354130 00350001 CrashDemo!_threadstartex+0xa2 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
006affec 00000000 00430e00 00355188 00000000 kernel32!BaseThreadStart+0x37


FOLLOWUP_IP: 
CrashDemo!TestFunc+2e [c:\tests\crashdemo\crashdemo.cpp @ 124]
004309de c70000000000     mov     dword ptr [eax],0x0

SYMBOL_STACK_INDEX:  0

FOLLOWUP_NAME:  MachineOwner

SYMBOL_NAME:  CrashDemo!TestFunc+2e

MODULE_NAME:  CrashDemo

IMAGE_NAME:  CrashDemo.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  43dc6ee7

&lt;b&gt;STACK_COMMAND:  .ecxr ; kb&lt;/b&gt;

FAILURE_BUCKET_ID:  ACCESS_VIOLATION_CrashDemo!TestFunc+2e

BUCKET_ID:  ACCESS_VIOLATION_CrashDemo!TestFunc+2e

Followup: MachineOwner
---------
&lt;/pre&gt;
&lt;p&gt;Pay attention to the blocks of the text shown in bold. The first block reports the address and the type of the exception. The second block reports the call stack. And the third block gives us additional information on how to access the exception information stored in the crash dump. 
&lt;p&gt;Now we know the place where the exception occurred, and can even see the call stack. It's time to get the values of function parameters and local variables. Before we start, let's pay attention to the third selected block of information reported by !analyze. To repeat, the block contains the following: &lt;pre&gt;STACK_COMMAND:  .ecxr ; kb
&lt;/pre&gt;
&lt;p&gt;We already know 'kb' command (it displays the call stack). But what is .ecxr? This command asks the debugger to switch the current context to the one stored in the crash dump's exception information. After we have executed .ecxr, and only after that, we can reliably get access to the call stack and the values of local variables at the moment when the exception was raised. 
&lt;p&gt;After we have asked the debugger to use the exception context, we can use 'dv' command to display the values of function parameters and local variables. Since we usually want to see this information for every function on the call stack, we should actually use '!for_each_frame dv /t' command (/t option asks 'dv' to show type information, which is also useful). (And of course, we have to remember that in optimized builds some local variables can be optimized away, enregistered, or reused to store other data throughout the lifetime of the function, and as a result the values reported by 'dv' command can be incorrect). 
&lt;p&gt;Here is the final command line for analysis of crash dumps with exception information: &lt;pre&gt;  cdb -z c:\myapp.dmp -logo out.txt -lines -c &amp;quot;!analyze -v;.ecxr;!for_each_frame dv /t;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;And here is the sample output of '!for_each_frame dv /t' command: &lt;pre&gt;_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
00 006afe88 0043096e CrashDemo!TestFunc+0x2e [c:\tests\crashdemo\crashdemo.cpp @ 124]
int * pParam = 0x00000000
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
01 006aff6c 00430f31 CrashDemo!WorkerThread+0x5e [c:\tests\crashdemo\crashdemo.cpp @ 115]
void * lpParam = 0x00000000
int * TempPtr = 0x00000000
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
02 006affa8 00430ea2 CrashDemo!_callthreadstartex+0x51 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
struct _tiddata * ptd = 0x00355188
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
03 006affb4 7c80b50b CrashDemo!_threadstartex+0xa2 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
void * ptd = 0x00355188
struct _tiddata * _ptd = 0x00000000
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
04 006affec 00000000 kernel32!BaseThreadStart+0x37
Unable to enumerate locals, HRESULT 0x80004005
Private symbols (symbols.pri) are required for locals.
Type &amp;quot;.hh dbgerr005&amp;quot; for details.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
00 006afe88 0043096e CrashDemo!TestFunc+0x2e [c:\tests\crashdemo\crashdemo.cpp @ 124]
&lt;/pre&gt;
&lt;p&gt;If the minidump does not include the complete contents of the target process' memory, the debugger will be able to analyze the dump only if it can find exactly the same versions of executable modules that were loaded by the target process. In some cases, you will have to help the debugger to locate those modules – by specifying the module search path. Detailed information about module search path and related issues can be found in this &lt;a href="http://www.debuginfo.com/matchmodules.html"&gt;article&lt;/a&gt;. 
&lt;p&gt;Now let's proceed to crash dumps without exception information. When we are starting to analyze such a dump, we usually want to know the call stacks of all threads. Here is how to get this information: &lt;pre&gt;  cdb -z c:\myapp.dmp -logo out.txt -lines -c &amp;quot;~*kb;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;What to do if we don't know whether the crash dump contains exception information or not? For minidumps, it is possible to use &lt;a href="http://www.debuginfo.com/tools/minidumpview.html"&gt;MiniDumpView&lt;/a&gt; to print the contents of the dump and see if it contains exception information. For old-style 'full user dumps', probably the only option is to start as if the dump contains exception information, and see if !analyze reports something meaningful. 
&lt;p&gt;There is an interesting special case – it is possible that the crash dump was created because of an unhandled exception, but does not contain exception information by some reason. It is still possible to find out the place where the exception occurred, with the help of the following procedure: 
&lt;ol&gt;
&lt;li&gt;Print the call stacks of all threads (using CDB command shown above). 
&lt;li&gt;Find out the thread whose call stack contains kernel32!UnhandledExceptionFilter function. 
&lt;li&gt;Use the fact that the first parameter of &lt;a href="http://msdn.microsoft.com/library/en-us/debug/base/unhandledexceptionfilter.asp"&gt;UnhandledExceptionFilter&lt;/a&gt; function contains a pointer to EXCEPTION_POINTERS structure. &lt;/ol&gt;
&lt;p&gt;Here is the declaration of EXCEPTION_POINTERS structure: &lt;pre&gt;typedef struct _EXCEPTION_POINTERS 
{  
    PEXCEPTION_RECORD ExceptionRecord;  
    PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
&lt;/pre&gt;
&lt;p&gt;If we know the address of this structure, we can take the pointer to the exception context (stored in ContextRecord field), pass it to .cxr command and thus switch the debugger context to the place where the exception occurred. After .cxr command has been executed, we can use, for example, 'kb' command to get the call stack at the moment of the exception. Here is an example: 
&lt;p&gt;1. Print call stacks of all threads. &lt;pre&gt;  cdb -z c:\myapp.dmp -logo out.txt -c &amp;quot;~*kb;q&amp;quot;
&lt;/pre&gt;&lt;pre&gt;0:000&amp;gt; ~*kb

.  0  Id: 6c4.73c Suspend: 1 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr  Args to Child              
0012fdf8 7c90d85c 7c8023ed 00000000 0012fe2c ntdll!KiFastSystemCallRet
0012fdfc 7c8023ed 00000000 0012fe2c 0012ff54 ntdll!NtDelayExecution+0xc
0012fe54 7c802451 0036ee80 00000000 0012ff54 kernel32!SleepEx+0x61
0012fe64 00430856 0036ee80 00330033 00300037 kernel32!Sleep+0xf
0012ff54 00431702 00000001 00352ed0 00352fb0 CrashDemo!wmain+0x96
0012ffb8 004314bd 0012fff0 7c816d4f 00330033 CrashDemo!__tmainCRTStartup+0x232
0012ffc0 7c816d4f 00330033 00300037 7ffd9000 CrashDemo!wmainCRTStartup+0xd
0012fff0 00000000 0042e5a5 00000000 00000000 kernel32!BaseProcessStart+0x23

   1  Id: 6c4.5cc Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr  Args to Child              
006af6e4 7c90e273 7c863130 d0000144 00000004 ntdll!KiFastSystemCallRet
006af6e8 7c863130 d0000144 00000004 00000000 ntdll!NtRaiseHardError+0xc
006af96c 00438951 &lt;b&gt;006af9e0&lt;/b&gt; 5d343834 00000000 &lt;b&gt;kernel32!UnhandledExceptionFilter+0x59c&lt;/b&gt;
006af990 00430f2a c0000005 006af9e0 0044ad30 CrashDemo!_XcptFilter+0x61
006af99c 0044ad30 00000000 00000000 00000000 CrashDemo!_callthreadstartex+0x7a
006af9b0 00438c67 00430f13 0049a230 00000000 CrashDemo!_EH4_CallFilterFunc+0x12
006af9e8 7c9037bf 006afad4 006aff98 006afaf0 CrashDemo!_except_handler4+0xb7
006afa0c 7c90378b 006afad4 006aff98 006afaf0 ntdll!ExecuteHandler2+0x26
006afabc 7c90eafa 00000000 006afaf0 006afad4 ntdll!ExecuteHandler+0x24
006afabc 004309be 00000000 006afaf0 006afad4 ntdll!KiUserExceptionDispatcher+0xe
006afe88 0043094e 00000000 00354130 00350001 CrashDemo!TestFunc+0x2e
006aff6c 00430f01 00000000 647bff58 00354130 CrashDemo!WorkerThread+0x5e
006affa8 00430e72 00000000 006affec 7c80b50b CrashDemo!_callthreadstartex+0x51
006affb4 7c80b50b 00355188 00354130 00350001 CrashDemo!_threadstartex+0xa2
006affec 00000000 00430dd0 00355188 00000000 kernel32!BaseThreadStart+0x37
&lt;/pre&gt;
&lt;p&gt;2. Change debugger context and get the call stack for the exception. &lt;pre&gt;  cdb -z c:\myapp.dmp -logo out.txt -lines -c &amp;quot;.cxr dwo(0x006af9e0+4);kb;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;('dwo' operator returns the double word stored at the specified address and passes it to .cxr command) 
&lt;p&gt;The &lt;a href="http://www.debuginfo.com/#batchfiles"&gt;batch files&lt;/a&gt; presented later in this article (DumpStackCtx.bat in particular) will simplify this task significantly. 
&lt;p&gt;There is an alternative approach that also allows to solve this problem – you can find more information about it &lt;a href="http://blogs.msdn.com/jmstall/archive/2005/01/18/355697.aspx"&gt;here&lt;/a&gt;. 
&lt;h6&gt;&lt;a&gt;Virtual memory analysis &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Another situation where CDB can significantly complement Visual Studio debugger is when we want to inspect virtual memory layout of the debuggee process. The following command will display the complete virtual memory map of the process: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;!vadump -v;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;(!vadump command is responsible for printing the virtual memory map, and -v option, as usual, asks it to show detailed output) 
&lt;p&gt;Here is an example of !vadump output: &lt;pre&gt;BaseAddress:       00040000
AllocationBase:    00040000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        0002e000
State:             00002000  MEM_RESERVE
Type:              00020000  MEM_PRIVATE

BaseAddress:       0006e000
AllocationBase:    00040000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000104  PAGE_READWRITE + PAGE_GUARD
Type:              00020000  MEM_PRIVATE

BaseAddress:       0006f000
AllocationBase:    00040000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        00011000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00020000  MEM_PRIVATE
&lt;/pre&gt;
&lt;p&gt;On Windows XP and Windows Server 2003, CDB offers a better command for inspecting virtual memory layout - !address. This command allows to perform the following tasks: 
&lt;ul&gt;
&lt;li&gt;Display virtual memory map of the process (in my opinion, in a more readable format than !vadump) 
&lt;li&gt;Display useful statistics about virtual memory usage 
&lt;li&gt;Determine the kind of virtual memory region the specified address belongs to (for example, does it belong to a stack, heap or an executable image?) &lt;/ul&gt;
&lt;p&gt;Here is how to use !address to report the virtual memory map: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;!address;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Here is a sample output that shows a memory region occupied by a thread's stack: &lt;pre&gt;    00040000 : 00040000 - 0002e000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000000 
                    State    00002000 MEM_RESERVE
                    Usage    RegionUsageStack
                    Pid.Tid  658.644
               0006e000 - 00001000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000104 PAGE_READWRITE | PAGE_GUARD
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  658.644
               0006f000 - 00011000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000004 PAGE_READWRITE
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    &lt;b&gt;Pid.Tid  658.644&lt;/b&gt;
&lt;/pre&gt;
&lt;p&gt;Note that !address is smart enough to report the thread id of the thread the stack belongs to. 
&lt;p&gt;After !address has finished reporting virtual memory regions, it also reports interesting statistics about virtual memory usage: &lt;pre&gt;-------------------- Usage SUMMARY --------------------------
    TotSize   Pct(Tots) Pct(Busy)   Usage
   00838000 : 0.40%       27.96%      : RegionUsageIsVAD
   7e28c000 : 98.56%      0.00%       : RegionUsageFree
   01348000 : 0.94%       65.60%      : RegionUsageImage
   00040000 : 0.01%       0.85%       : RegionUsageStack
   00001000 : 0.00%       0.01%       : RegionUsageTeb
   001a0000 : 0.08%       5.53%       : RegionUsageHeap
   00000000 : 0.00%       0.00%       : RegionUsagePageHeap
   00001000 : 0.00%       0.01%       : RegionUsagePeb
   00001000 : 0.00%       0.01%       : RegionUsageProcessParametrs
   00001000 : 0.00%       0.01%       : RegionUsageEnvironmentBlock
       Tot: 7fff0000 Busy: 01d64000

-------------------- Type SUMMARY --------------------------
    TotSize   Pct(Tots) Usage
   7e28c000 : 98.56%     : &amp;lt;free&amp;gt;
   01348000 : 0.94%      : MEM_IMAGE
   007b6000 : 0.38%      : MEM_MAPPED
   00266000 : 0.12%      : MEM_PRIVATE

-------------------- State SUMMARY --------------------------
    TotSize   Pct(Tots) Usage
   01647000 : 1.09%      : MEM_COMMIT
   7e28c000 : 98.56%     : MEM_FREE
   0071d000 : 0.35%      : MEM_RESERVE

Largest free region: Base 01014000 - Size 59d5c000
&lt;/pre&gt;
&lt;p&gt;The statistics can be especially useful when we are debugging a memory leak and want to determine what kind of memory is leaked (heap, stack, raw virtual memory, and so on). The last item allows to determine the size of the largest free region of virtual memory, which can be helpful when we have to design an application with high memory demands. 
&lt;p&gt;If you only want to see the statistics and do not need the virtual memory map, you can use -summary parameter: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;!address -summary;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;If we need to determine what kind of virtual memory the given address belongs to, we can pass this address as a parameter to !address command. Here is an example: &lt;pre&gt;0:000&amp;gt; !address 0x000a2480;q
    000a0000 : 000a0000 - 000d7000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000004 PAGE_READWRITE
                    State    00001000 MEM_COMMIT
                    Usage    &lt;b&gt;RegionUsageHeap&lt;/b&gt;
                    Handle   000a0000
&lt;/pre&gt;
&lt;h6&gt;&lt;a&gt;Searching for symbols &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Sometimes we might need to determine the address of a symbol (function or variable). If we know the exact name of the symbol, we can enter it into Disassembly window of Visual Studio debugger and get the address. But what if we don't remember the exact name? Or want to find the addresses of a set of symbols with the same pattern in the name (for example, all member functions of a class)? CDB can easily solve this problem – it offers 'x' command, which can list all symbols whose names match the specified mask: &lt;pre&gt;  x Module!Symbol
&lt;/pre&gt;
&lt;p&gt;The following command tries to locate the address of UnhandledExceptionFilter function, located in kernel32.dll: &lt;pre&gt;  cdb -pv -pn notepad.exe -logo out.txt -c &amp;quot;x kernel32!UnhandledExceptionFilter;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Here is the output: &lt;pre&gt;0:000&amp;gt; x kernel32!UnhandledExceptionFilter;q
7c862b8a kernel32!UnhandledExceptionFilter = &amp;lt;no type information&amp;gt;
&lt;/pre&gt;
&lt;p&gt;'x' command accepts a large number of possible wildcards, and offers some useful options for sorting the output and for additional information about symbols - you can find more information about it in WinDbg documentation. For example, the following command lists all member functions and static data members of CMainFrame class defined in our application's main executable: &lt;pre&gt;0:000&amp;gt; x myapp!*CMainFrame*
004542f8 MyApp!CMainFrame::classCMainFrame = struct CRuntimeClass
00401100 MyApp!CMainFrame::`scalar deleting destructor' (void)
004011a0 MyApp!CMainFrame::OnCreate (struct tagCREATESTRUCTW *)
00401000 MyApp!CMainFrame::CreateObject (void)
00401280 MyApp!CMainFrame::PreCreateWindow (struct tagCREATESTRUCTW *)
00401070 MyApp!CMainFrame::GetRuntimeClass (void)
00401120 MyApp!CMainFrame::~CMainFrame (void)
00401090 MyApp!CMainFrame::CMainFrame (void)
00401080 MyApp!CMainFrame::GetMessageMap (void)
004578ec MyApp!CMainFrame::`RTTI Base Class Array' = &amp;lt;no type information&amp;gt;
004578dc MyApp!CMainFrame::`RTTI Class Hierarchy Descriptor' = &amp;lt;no type information&amp;gt;
004578c8 MyApp!CMainFrame::`RTTI Complete Object Locator' = &amp;lt;no type information&amp;gt;
004579ec MyApp!CMainFrame::`RTTI Base Class Descriptor at (0,-1,0,64)' = &amp;lt;no type information&amp;gt;
00461e94 MyApp!CMainFrame `RTTI Type Descriptor' = &amp;lt;no type information&amp;gt;
00454354 MyApp!CMainFrame::`vftable' = &amp;lt;no type information&amp;gt;
&lt;/pre&gt;
&lt;p&gt;CDB can also do just the opposite – find symbol by address, using 'ln' command: &lt;pre&gt;  ln Address
&lt;/pre&gt;
&lt;p&gt;Here is how to use it: &lt;pre&gt;  cdb -pv -pn notepad.exe -logo out.txt -c &amp;quot;ln 0x77d491c8;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Here is the output: &lt;pre&gt;0:000&amp;gt; ln 0x77d491c8;q
(77d491c6)   USER32!GetMessageW+0x2   |  (77d49216)   USER32!CharUpperBuffW
&lt;/pre&gt;
&lt;p&gt;Note that we do not have to specify the start address of the symbol (a function in this case), but can use any address inside the address range occupied by the symbol. 'ln' will find the symbol, report its address, and in addition report the address and the name of the symbol that follows the specified one. 
&lt;h6&gt;&lt;a&gt;Displaying data structures &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;If we want to explore the contents of a data structure, we usually use Visual Studio's Watch, QuickWatch or other similar window. These windows allow us to see the types and values of the structure's member variables. But what if we also need to know the exact layout of the structure, including the offsets of its members? Visual Studio does not offer an easy-to-use solution, but fortunately CDB does. With the help of 'dt' command, we can display the exact layout of a data structure or a class. 
&lt;p&gt;If we simply want to know the layout of a data type, we can use this command as follows: &lt;pre&gt;  dt -b TypeName
&lt;/pre&gt;
&lt;p&gt;(-b option enables recursive display of embedded data structures for members whose type is also a structure or a class). 
&lt;p&gt;Here is a sample CDB command line: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;dt -b CSymbolInfoPackage;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Here is the output (obtained while running &lt;a href="http://www.debuginfo.com/examples/src/SymFromAddr.cpp"&gt;SymFromAddr&lt;/a&gt; application): &lt;pre&gt;0:000&amp;gt; dt /b CSymbolInfoPackage;q
   +0x000 si               : _SYMBOL_INFO
      +0x000 SizeOfStruct     : Uint4B
      +0x004 TypeIndex        : Uint4B
      +0x008 Reserved         : Uint8B
      +0x018 Index            : Uint4B
      +0x01c Size             : Uint4B
      +0x020 ModBase          : Uint8B
      +0x028 Flags            : Uint4B
      +0x030 Value            : Uint8B
      +0x038 Address          : Uint8B
      +0x040 Register         : Uint4B
      +0x044 Scope            : Uint4B
      +0x048 Tag              : Uint4B
      +0x04c NameLen          : Uint4B
      +0x050 MaxNameLen       : Uint4B
      +0x054 Name             : Char
   +0x058 name             : Char
&lt;/pre&gt;
&lt;p&gt;If you want to display the layout of a particular variable, you can pass its address to 'dt' command: &lt;pre&gt;  dt -b TypeName Address
&lt;/pre&gt;
&lt;p&gt;Here is a sample: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;dt -b CSymbolInfoPackage 0x0012f6d0;q&amp;quot;
&lt;/pre&gt;&lt;pre&gt;0:000&amp;gt; dt /b CSymbolInfoPackage 0x0012f6d0;q
   +0x000 si               : _SYMBOL_INFO
      +0x000 SizeOfStruct     : 0x58
      +0x004 TypeIndex        : 2
      +0x008 Reserved         : 
       [00] 0
       [01] 0
      +0x018 Index            : 1
      +0x01c Size             : 0x428
      +0x020 ModBase          : 0x400000
      +0x028 Flags            : 0
      +0x030 Value            : 0
      +0x038 Address          : 0x411d30
      +0x040 Register         : 0
      +0x044 Scope            : 0
      +0x048 Tag              : 5
      +0x04c NameLen          : 0xe
      +0x050 MaxNameLen       : 0x7d1
      +0x054 Name             :  &amp;quot;S&amp;quot;
       [00] 83 'S'
   +0x058 name             :  &amp;quot;SymbolInfo&amp;quot;
    [00] 83 'S'
    [01] 121 'y'
    [02] 109 'm'
    [03] 98 'b'
    [04] 111 'o'
    [05] 108 'l'
    [06] 73 'I'
    [07] 110 'n'
    [08] 102 'f'
    [09] 111 'o'
    [10] 0 ''
    [11] 0 ''
    [12] 0 ''
    [13] 0 ''
    [14] 0 ''
    [15] 0 ''
    [16] 0 ''
    [17] 0 ''
    ... part of the output omitted
    [1990] 0 ''
    [1991] 0 ''
    [1992] 0 ''
    [1993] 0 ''
    [1994] 0 ''
    [1995] 0 ''
    [1996] 0 ''
    [1997] -52 ''
    [1998] -52 ''
    [1999] -52 ''
    [2000] -52 ''
&lt;/pre&gt;
&lt;p&gt;Note that now 'dt' also shows the values of the structure's member variables. 
&lt;h5&gt;&lt;a&gt;Batch files &lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Now we know how to use CDB to solve some interesting debugging problems. It's time to solve one more problem – replace long CDB command lines with easy-to-use batch files. Consider the command we used as a sample at the beginning of the article: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;lm;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Most parts of this command are static and cannot change. The only variable part is the target information (-pn myapp.exe), where we might need to use another executable name, or even another way of attaching (e.g., by process id). 
&lt;p&gt;Here is how this command can be represented in a batch file: &lt;pre&gt;  ; lm.bat
  cdb -pv %1 %2 -logo out.txt -c &amp;quot;lm;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;If we want to run this batch file to get the list of modules loaded by a process, we can use either one of the following commands: 
&lt;p&gt;Attach by executable name: &lt;pre&gt;  lm -pn myapp.exe
&lt;/pre&gt;
&lt;p&gt;Attach by process id: &lt;pre&gt;  lm -p 1234
&lt;/pre&gt;
&lt;p&gt;Attach by service name: &lt;pre&gt;  lm -psn MyService
&lt;/pre&gt;
&lt;p&gt;Open a crash dump file: &lt;pre&gt;  lm -z c:\myapp.dmp
&lt;/pre&gt;
&lt;p&gt;Regardless of the target specified, the command still does the same – prints the list of loaded modules. 
&lt;p&gt;If we need to specify additional parameters to a CDB command, we can do it using the same approach. Consider the following command, which can be used to display the layout of a data structure: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;dt /b MyStruct;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Of course, we want to use this command with any data type, not only with MyStruct. Here is how we can do it: &lt;pre&gt;  ; dt.bat
  cdb -pv %1 %2 -logo out.txt -c &amp;quot;dt /b %3;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Now we can run the command like this: &lt;pre&gt;  dt -pn myapp.exe CTestClass
&lt;/pre&gt;
&lt;p&gt;Or like this: &lt;pre&gt;  dt -p 1234 SYMBOL_INFO
&lt;/pre&gt;
&lt;p&gt;Or, for example, like this: &lt;pre&gt;  dt -z c:\myapp.dmp EXCEPTION_POINTERS
&lt;/pre&gt;
&lt;p&gt;We can use the same approach with many other commands. &lt;a href="http://www.debuginfo.com/tools/cdbbatch.html"&gt;Here&lt;/a&gt; you can find the list of batch files that wrap the commands discussed in this article. In the future, I am going to extend it with other useful commands. &lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+windbg+the+easy+way(part2)&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!899.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!899.entry</guid><pubDate>Tue, 19 Jun 2007 11:46:53 GMT</pubDate><slash:comments>1</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!899/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!899.entry#comment</wfw:comment><dcterms:modified>2007-06-19T11:46:53Z</dcterms:modified></item><item><title>windbg the easy way (part1)</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!898.entry</link><description>&lt;p&gt;Updated: 16.02.2006  &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.debuginfo.com/#intro"&gt;Introduction&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#setupandconfig"&gt;Setup and configuration&lt;/a&gt;  &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.debuginfo.com/#setup"&gt;Setup&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#sympath"&gt;Symbol server path&lt;/a&gt;&lt;/ul&gt; &lt;li&gt;&lt;a href="http://www.debuginfo.com/#cmdlinebasics"&gt;CDB command line basics&lt;/a&gt;  &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.debuginfo.com/#startsession"&gt;Starting the debugging session&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#endsession"&gt;Ending the debugging session&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#runcommands"&gt;Running commands&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#noninvasivedebug"&gt;Noninvasive debugging&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#savelogfile"&gt;Saving output to log file&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#sourcelineinfo"&gt;Source line information&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#cdbandproxyservers"&gt;CDB and proxy servers&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#startupmessages"&gt;Startup messages&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#summary"&gt;Summary&lt;/a&gt;&lt;/ul&gt; &lt;li&gt;&lt;a href="http://www.debuginfo.com/#solvereallife"&gt;Solving real life problems&lt;/a&gt;  &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.debuginfo.com/#debugdeadlocks"&gt;Debugging deadlocks&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#debughighcpu"&gt;Debugging high CPU utilization&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/#debugstackoverflow"&gt;Debugging stack overflow&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/easywindbg2.html#savedumps"&gt;Saving dumps&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/easywindbg2.html#crashdumpanalysis"&gt;Crash dump analysis&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/easywindbg2.html#virtmemanalysis"&gt;Virtual memory analysis&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/easywindbg2.html#searchsymbols"&gt;Searching for symbols&lt;/a&gt;  &lt;li&gt;&lt;a href="http://www.debuginfo.com/easywindbg2.html#displaydatastruct"&gt;Displaying data structures&lt;/a&gt;&lt;/ul&gt; &lt;li&gt;&lt;a href="http://www.debuginfo.com/easywindbg2.html#batchfiles"&gt;Batch files&lt;/a&gt;&lt;/ul&gt; &lt;h5&gt;&lt;a&gt;Introduction &lt;/a&gt;&lt;/h5&gt; &lt;p&gt;What is your favourite debugger? If you would ask me this question, I would probably reply that it is “Visual Studio + WinDbg”. I like Visual Studio for its natural and productive interface. I like that it allows me to get the necessary information quickly and, well, visually. But unfortunately, some kinds of information cannot be easily obtained with Visual Studio debugger. For example, what if I need to know which thread is holding a particular critical section? Or which function occupies most of the space on the stack? Here comes WinDbg. Its commands can provide answers to these and many other interesting questions that arise during debugging sessions. And I even do not have to close Visual Studio to attach WinDbg to the target application – thanks to WinDbg's support for noninvasive debugging (discussed later in this article), we can take advantage of Visual Studio GUI and WinDbg commands at the same time.  &lt;p&gt;The only problem is that WinDbg is not too easy to use. It takes time to adapt to its user interface, and even more time to master the commands. But what if you need it today, right now, to debug an urgent problem? Is there a quick and easy way? Yes, there is. CDB, the little brother of WinDbg, exposes the same functionality with a simple, command line based interface. In this article, I will show you how to take advantage of CDB and start using it right now to complement Visual Studio debugger. You will see how to setup and configure CDB, and how to use it to solve real life problems. In addition, I will provide you with a set of batch files, which will hide most of the remaining complexities of CDB's command line interface and save you a lot of typing.  &lt;h5&gt;&lt;a&gt;Setup and configuration &lt;/a&gt;&lt;/h5&gt; &lt;h6&gt;&lt;a&gt;Setup &lt;/a&gt;&lt;/h6&gt; &lt;p&gt;Of course, before we can start using CDB, we have to install and configure it. WinDbg and CDB are distributed as part of &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx"&gt;Debugging Tools for Windows&lt;/a&gt; package, which can be downloaded &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx"&gt;here&lt;/a&gt;. The installation is simple, and unless you are going to develop applications with the help of WinDbg SDK, you can simply accept the default settings. (But if you are going to use the SDK, you have to select custom setup and enable SDK installation; it is also recommended to use an installation directory whose name does not contain spaces). After the installation has been completed, the installation directory should contain all the necessary files, including WinDbg (windbg.exe) and CDB (cdb.exe).  &lt;p&gt;Debugging Tools also support “xcopy” style of installation. After you have installed it on one machine, you do not necessarily have to run the setup again to install it on other machines. It is enough to collect all the files in the installation directory and copy them onto the target machine or a network share.  &lt;h6&gt;&lt;a&gt;Symbol server path &lt;/a&gt;&lt;/h6&gt; &lt;p&gt;Some important WinDbg commands cannot function properly without access to up-to-date symbols for operating system DLLs. In the past, we could obtain the necessary symbols by downloading a large package from Microsoft's FTP server. It was time consuming, and symbols could easily become outdated (and therefore useless) after installing an update for the operating system. Fortunately, nowadays there is a much simpler way to obtain symbols – symbol server. This technology, supported by WinDbg and Visual Studio debuggers, allows to download up-to-date symbols on demand from a server maintained by Microsoft. With symbol server, we do not have to download the complete symbol package, because the debugger knows which DLLs it is going to inspect, and therefore can download symbols only for those DLLs. If symbols become outdated after an operating system update has been installed, the debugger will notice it and download the necessary symbols again.  &lt;p&gt;To make symbol server feature work, we should let the debugger know the path to the symbol server. The simplest way to do it is to specify the symbol server path in _NT_SYMBOL_PATH environment variable. The following path should be used: &amp;quot;srv*c:\symbolcache*http://msdl.microsoft.com/download/symbols&amp;quot; (c:\symbolcache directory will be used as a cache for symbol files downloaded from the symbol server; you can use any local or network directory path that is valid on your system). For example: &lt;pre&gt;  set _NT_SYMBOL_PATH=srv*c:\symbols*http://msdl.microsoft.com/download/symbols
&lt;/pre&gt;
&lt;p&gt;After you have set _NT_SYMBOL_PATH environment variable to the proper value, symbol server feature is ready for use. More information about symbol server technology, related settings and, if necessary, troubleshooting tips can be found in WinDbg documentation (Debuggers | Symbols section). 
&lt;p&gt;Additional configuration steps are needed to access symbol server from behind a proxy server that requires you to log in. See &lt;a href="http://www.debuginfo.com/#cdbandproxyservers"&gt;CDB and proxy servers&lt;/a&gt; section of this article for more information. 
&lt;h5&gt;&lt;a&gt;CDB command line basics &lt;/a&gt;&lt;/h5&gt;
&lt;h6&gt;&lt;a&gt;Starting the debugging session &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;When we start learning a new debugger, the first question usually is: how to start the debugging session? As most debuggers do, CDB allows us to start a new instance of the application or attach to an already running process. Starting a new instance is as simple as the following: &lt;pre&gt;  cdb c:\myapp.exe
&lt;/pre&gt;
&lt;p&gt;If we want to attach to an already running process, one of the following options can be used: 
&lt;p&gt;Options&lt;br&gt;Description&lt;br&gt;Example 
&lt;p&gt;-p Pid&lt;br&gt;This option allows to attach to the process with the specified process id. The process id can be obtained from Task Manager or other similar tool. &lt;br&gt;cdb -p 1034 
&lt;p&gt;-pn ExeName&lt;br&gt;This option allows to attach to the process with the specified name of its main executable file (.exe). This option is usually more convenient than “-p Pid”, because we usually know the name of our application's main executable, and do not have to look for it in Task Manager. But this option cannot be used if more than one process with the given executable name is currently running (CDB will report an error). &lt;br&gt;cdb -pn myapp.exe 
&lt;p&gt;-psn ServiceName&lt;br&gt;This option allows to attach to the process that contains the specified service. For example, if you want to attach to, say, Windows Management Instrumentation service, you should use WinMgmt as the service name. &lt;br&gt;cdb -psn MyService 
&lt;p&gt;CDB can also be used to analyze crash dumps. To open a crash dump, use -z option: &lt;pre&gt;  cdb -z DumpFile
&lt;/pre&gt;
&lt;p&gt;For example: &lt;pre&gt;  cdb -z c:\myapp.dmp
&lt;/pre&gt;
&lt;h6&gt;&lt;a&gt;Ending the debugging session &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;After we have started a new debugging session, CDB displays its own command prompt. You can use this prompt to enter and execute any command supported by CDB. 
&lt;p&gt;&lt;a href="http://byfiles.storage.msn.com/y1pvIUwXXcLuQJ9xfP3wtQAQNhLWCE1oS_V7qpH1w1Njnx2TxrIMJClrOjAD-DaV00L"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px" height=337 alt=cdbconsole src="http://byfiles.storage.msn.com/y1pvIUwXXcLuQLbjng1i03vS8Ch-KeY81kq1Q4SW_r7qM3BhnQsN8-kYsSsryRpGW95" width=671 border=0&gt;&lt;/a&gt; 
&lt;p&gt;'q' command ends debugging session and exits CDB: &lt;pre&gt;0:000&amp;gt; q
quit:
&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Warning: When you end the debugging session and exit CDB, the debuggee will also be terminated by the operating system. If you want to exit CDB and keep the debuggee running, you can use .detach command (supported only in Windows XP and newer operating systems), or use CDB in noninvasive mode (discussed below). 
&lt;h6&gt;&lt;a&gt;Running commands &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;While it is possible to use CDB command prompt to execute debugger commands, it is often faster to specify the necessary commands on the command line, using -c option. &lt;pre&gt;  cdb -pn myapp.exe -c &amp;quot;command1;command2&amp;quot;
&lt;/pre&gt;
&lt;p&gt;(commands are separated with semicolons) 
&lt;p&gt;For example, the following command line will attach CDB to our application, display the list of loaded modules, and exit: &lt;pre&gt;  cdb -pn myapp.exe -c &amp;quot;lm;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Note the use of 'q' command at the end of the command list – it allows us to automatically close CDB after all debugger commands have been executed. 
&lt;h6&gt;&lt;a&gt;Noninvasive debugging &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;By default, when we use CDB to debug an already running process, it attaches as a fully functional debugger (using Win32 Debugging API). It is possible to set breakpoints, step through the code, get notified about various debugging events (such as exceptions, module load/unload, thread start/exit, and so on). But Visual Studio debugger allows us to do the same, and offers a much better user interface. In addition, only one debugger can be attached to the process at a time. Does it mean that if we are already debugging an application with Visual Studio debugger, we cannot use CDB to obtain additional information about the same application? No, it doesn't, because CDB also supports noninvasive debugging mode. 
&lt;p&gt;When CDB attaches to the target process in noninvasive mode, it does not use Win32 Debugging API. Instead, it simply suspends all threads in the target process and starts executing commands specified by the user. After all commands have been executed, just before CDB itself terminates, it resumes the suspended threads. As a result, the target process can continue running as if it wasn't debugged at all. Even if the target process is already being debugged by a fully functional debugger like Visual Studio, CDB still can attach to it in noninvasive mode and obtain the necessary information. After CDB has finished its work and detached, we can continue debugging the application in Visual Studio debugger. 
&lt;p&gt;How to use CDB in noninvasive mode? Using -pv command line option. For example, the following command will attach to our application noninvasively, display the list of loaded modules, and exit. The application will continue running. &lt;pre&gt;  cdb -pv -pn myapp.exe -c &amp;quot;lm;q&amp;quot;
&lt;/pre&gt;
&lt;h6&gt;&lt;a&gt;Saving output to log file &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;The output of CDB commands can be long, and it can be inconvenient to read it from the console window. It would be much better to save the output to a log file, and CDB allows us to do it with the help of -loga and -logo options ('-loga &amp;lt;filename&amp;gt;' appends the output to the end of the specified file, while '-logo &amp;lt;filename&amp;gt;' overwrites the file if it already exists). 
&lt;p&gt;Lets enhance our sample command (that lists modules in the target process) with logging capability and save the output to out.txt file in the current directory: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;lm;q&amp;quot;
&lt;/pre&gt;
&lt;h6&gt;&lt;a&gt;Source line information &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Another important command line option exposed by CDB is -lines. This option turns on the source line information support, which, for example, allows CDB to display source file names and line numbers when reporting call stacks. (By default, source line support is turned off, and CDB does not display source file/line information). 
&lt;h6&gt;&lt;a&gt;CDB and proxy servers &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;If you are going to use CDB from behind a proxy server that requires you to log in, symbol server access will not work by default. The reason is that in the default configuration CDB is not allowed to show proxy server's login prompt when it is trying to connect to the symbol server. To change this behavior and make symbol server access work, two commands should be added to the beginning of the command list: &lt;pre&gt;  !sym prompts;.reload
&lt;/pre&gt;
&lt;p&gt;For example: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;!sym prompts;.reload;lm;q&amp;quot;
&lt;/pre&gt;
&lt;h6&gt;&lt;a&gt;Startup messages &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;When CDB launches a new application, attaches to an existing process, or opens a crash dump, it shows a sequence of startup messages. These messages are followed by the output of CDB commands (which can be specified using -c option, or entered manually). Usually the startup messages serve only informational purposes; but if something goes wrong, they will contain the description of the problem, sometimes followed by recommendations on how to solve it. 
&lt;p&gt;For example, the following output contains a message that informs us that symbol path is not set, and as a result some debugger commands may not work: &lt;pre&gt;D:\Progs\DbgTools&amp;gt;cdb myapp.exe

Microsoft (R) Windows Debugger  Version 6.5.0003.7
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: myapp.exe
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path.           *
* Use .symfix to have the debugger choose a symbol path.                   *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
&lt;/pre&gt;
&lt;h6&gt;&lt;a&gt;Summary &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Here is the list of CDB command line templates that we will use throughout the remainder of this article (we will always use the same templates, and usually only the list of commands inside -c option will change, depending on the problem we are trying to solve). 
&lt;p&gt;Attach to a running process (by process id) in noninvasive mode, execute a set of commands and save the output in out.txt file: &lt;pre&gt;  cdb -pv -p &amp;lt;processid&amp;gt; -logo out.txt -lines -c &amp;quot;command1;command2;...;commandN;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Attach to a running process (by executable name) in noninvasive mode, execute a set of commands and save the output in out.txt file: &lt;pre&gt;  cdb -pv -pn &amp;lt;exename&amp;gt; -logo out.txt -lines -c &amp;quot;command1;command2;...;commandN;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Attach to a running process (by service name) in noninvasive mode, execute a set of commands and save the output in out.txt file: &lt;pre&gt;  cdb -pv -psn &amp;lt;servicename&amp;gt; -logo out.txt -lines -c &amp;quot;command1;command2;...;commandN;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Open a crash dump file, execute a set of commands and save the output to out.txt file: &lt;pre&gt;  cdb -z &amp;lt;dumpfile&amp;gt; -logo out.txt -lines -c &amp;quot;command1;command2;...;commandN;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;If we are going to use CDB from behind a proxy server that requires us to login, two additional commands should be added to make symbol server access work. For example: &lt;pre&gt;  cdb -pv -pn &amp;lt;exename&amp;gt; -logo out.txt -lines -c &amp;quot;!sym prompts;.reload;command1;command2;...;commandN;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Looks like a lot of typing? Not really. Later in this article I will present a set of batch files, which will hide the repeating command line options and minimize the amount of information you have to enter manually. 
&lt;h5&gt;&lt;a&gt;Solving real life problems &lt;/a&gt;&lt;/h5&gt;
&lt;h6&gt;&lt;a&gt;Debugging deadlocks &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;When our application appears hung or unresponsive, the natural question is: what is it currently doing? Where it got stuck? Of course, we can attach Visual Studio debugger to the application and inspect the call stacks of all threads. But we can do the same with CDB, and much quicker. The following command attaches CDB to the application noninvasively, prints all call stacks to the console and to the log file, and exits: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -lines -c &amp;quot;~*kb;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;('kb' command asks CDB to print the call stack of the current thread; '~*' prefix asks the debugger to repeat 'kb' for all existing threads in the process). 
&lt;p&gt;&lt;a href="http://www.debuginfo.com/examples/src/DeadLockDemo.cpp"&gt;DeadLockDemo.cpp&lt;/a&gt; file contains a sample application that demonstrates a typical deadlock scenario. If you compile it and run, its worker threads will get stuck very soon. If we run the abovementioned command to see what the application's threads are doing, we will see something like the following (here, and in the future, startup messages are omitted): &lt;pre&gt;.  0  Id: 6fc.4fc Suspend: 1 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr  Args to Child              
0012fdf8 7c90d85c 7c8023ed 00000000 0012fe2c ntdll!KiFastSystemCallRet
0012fdfc 7c8023ed 00000000 0012fe2c 0012ff54 ntdll!NtDelayExecution+0xc
0012fe54 7c802451 0036ee80 00000000 0012ff54 kernel32!SleepEx+0x61
0012fe64 004308a9 0036ee80 a0f63080 01c63442 kernel32!Sleep+0xf
0012ff54 00432342 00000001 003336e8 003337c8 DeadLockDemo!wmain+0xd9 
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 154]
0012ffb8 004320fd 0012fff0 7c816d4f a0f63080 DeadLockDemo!__tmainCRTStartup+0x232 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\crt0.c @ 318]
0012ffc0 7c816d4f a0f63080 01c63442 7ffdd000 DeadLockDemo!wmainCRTStartup+0xd 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\crt0.c @ 187]
0012fff0 00000000 0042e5aa 00000000 78746341 kernel32!BaseProcessStart+0x23

   1  Id: 6fc.&lt;b&gt;3d8&lt;/b&gt; Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr  Args to Child              
005afc14 7c90e9c0 7c91901b 000007d4 00000000 ntdll!KiFastSystemCallRet
005afc18 7c91901b 000007d4 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc
005afca0 7c90104b 004a0638 00430b7f 004a0638 ntdll!RtlpWaitForCriticalSection+0x132
005afca8 00430b7f 004a0638 005afe6c 005afe78 ntdll!RtlEnterCriticalSection+0x46
005afd8c 00430b15 005aff60 005afe78 003330a0 DeadLockDemo!CCriticalSection::Lock+0x2f 
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 62]
005afe6c 004309f1 004a0638 f3d065d5 00334fc8 DeadLockDemo!CCritSecLock::CCritSecLock+0x35 
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 90]
005aff6c 004311b1 00000000 f3d06511 00334fc8 DeadLockDemo!ThreadOne+0xa1 
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 182]
005affa8 00431122 00000000 005affec 7c80b50b DeadLockDemo!_callthreadstartex+0x51 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
005affb4 7c80b50b 003330a0 00334fc8 00330001 DeadLockDemo!_threadstartex+0xa2 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
005affec 00000000 00431080 003330a0 00000000 kernel32!BaseThreadStart+0x37

   2  Id: 6fc.&lt;b&gt;284&lt;/b&gt; Suspend: 1 Teb: 7ffdc000 Unfrozen
ChildEBP RetAddr  Args to Child              
006afc14 7c90e9c0 7c91901b 000007d8 00000000 ntdll!KiFastSystemCallRet
006afc18 7c91901b 000007d8 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc
006afca0 7c90104b 004a0620 00430b7f 004a0620 ntdll!RtlpWaitForCriticalSection+0x132
006afca8 00430b7f 004a0620 006afe6c 006afe78 ntdll!RtlEnterCriticalSection+0x46
006afd8c 00430b15 006aff60 006afe78 003332e0 DeadLockDemo!CCriticalSection::Lock+0x2f 
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 62]
006afe6c 00430d11 004a0620 f3e065d5 00334fc8 DeadLockDemo!CCritSecLock::CCritSecLock+0x35 
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 90]
006aff6c 004311b1 00000000 f3e06511 00334fc8 DeadLockDemo!ThreadTwo+0xa1 
  [c:\tests\deadlockdemo\deadlockdemo.cpp @ 202]
006affa8 00431122 00000000 006affec 7c80b50b DeadLockDemo!_callthreadstartex+0x51 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
006affb4 7c80b50b 003332e0 00334fc8 00330001 DeadLockDemo!_threadstartex+0xa2 
  [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 331]
006affec 00000000 00431080 003332e0 00000000 kernel32!BaseThreadStart+0x37
&lt;/pre&gt;
&lt;p&gt;The call stack (and source line numbers) suggest that ThreadOne is holding critical section CritSecOne and is waiting for critical section CritSecTwo, while ThreadTwo is holding critical section CritSecTwo and is waiting for critical section CritSecOne. This is an example of the classical “lock acquisition order” deadlock, where two threads need to acquire the same set of synchronization objects and do it in different order. If you want to avoid deadlocks of this kind, make sure that all threads acquire the necessary synchronization objects in the same order (in the sample, both ThreadOne and ThreadTwo could agree to acquire CritSecOne first and CritSecTwo next to avoid the deadlock). 
&lt;p&gt;By default, 'kb' command displays only the first 20 frames of the call stack. If you want to see a larger number of stack frames, you can explicitly override this limit (e.g., 'kb100' command asks the debugger to display up to 100 stack frames). In a live WinDbg session, it is also possible to use .kframes command to change the default limit for all subsequent commands. 
&lt;p&gt;Our sample application contained only three simple threads, and it wasn't difficult to identify the ones responsible for the deadlock. In large applications, it can be more difficult to identify the suspicious threads and prove their guilt. How should we approach it? In most cases, we already know a thread that isn't functioning properly (otherwise, how could we notice that the application is misbehaving?). Usually this thread is waiting on a synchronization object that is not available by some reason. Why is this object not available? Very often we can answer this question if we know which thread is currently holding this object (owns it, in other words). If the object happens to be a critical section, !locks command can help us to identify its current owner. When used without parameters, this command displays the list of critical sections that are currently held by the application's threads. Free critical sections are not included in the output. 
&lt;p&gt;Let's see !locks command in action: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -lines -c &amp;quot;!locks;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Here is the output of this command (also for &lt;a href="http://www.debuginfo.com/examples/src/DeadLockDemo.cpp"&gt;DeadLockDemo.cpp&lt;/a&gt; sample): &lt;pre&gt;CritSec DeadLockDemo!CritSecOne+0 at 004A0620
LockCount          1
RecursionCount     1
&lt;b&gt;OwningThread       3d8&lt;/b&gt;
EntryCount         1
ContentionCount    1
*** Locked

CritSec DeadLockDemo!CritSecTwo+0 at 004A0638
LockCount          1
RecursionCount     1
&lt;b&gt;OwningThread       284&lt;/b&gt;
EntryCount         1
ContentionCount    1
*** Locked

Scanned 40 critical sections
&lt;/pre&gt;
&lt;p&gt;Looking at the output of !locks command (OwningThread field in particular), we can conclude that critical section CritSecOne is held by the thread whose id is 0x3d8, and critical section CritSecTwo is held by thread 0x284. The output of 'kb' command (in the previous picture) allows to identify the threads with these ids. 
&lt;p&gt;If the application uses other kinds of synchronization objects (e.g. mutexes), it is more difficult to identify their owners (kernel debugger is required), and I will reserve it for a future article. 
&lt;h6&gt;&lt;a&gt;Debugging high CPU utilization &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;For most kinds of software applications, too high CPU consumption (up to 100% on a single-CPU system, according to Task Manager) is a clear sign of a bug. Usually it means that one of the application's threads has entered an infinite loop. Of course, the natural way to debug this problem is to attach Visual Studio debugger to the process and check what the offending thread is doing. But how can we determine which thread to check? CDB offers us an easy and convenient solution - !runaway command. When used without parameters, this command displays the times spent by each of the application's threads executing user mode code (additional parameters can also show the times spent in kernel mode, and the times elapsed since the moment when a thread was started). 
&lt;p&gt;Here is how to use this command with CDB: &lt;pre&gt;  cdb -pv -pn myapp.exe -logo out.txt -c &amp;quot;!runaway;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Here is a sample output of !runaway command: &lt;pre&gt;0:000&amp;gt; !runaway
 User Mode Time
  Thread       Time
   1:358       0 days 0:00:47.408
   2:150       0 days 0:00:03.495
   0:d8        0 days 0:00:00.000
&lt;/pre&gt;
&lt;p&gt;It looks like the thread with id 0x358 utilizes most of the CPU time. But this information is not yet enough to prove that thread 0x358 is guilty, because the command displays the CPU time spent by the thread during its whole lifetime. What we need is to see how the threads' CPU times change. Let's run the same command again. This time, we could see something like the following: &lt;pre&gt;0:000&amp;gt; !runaway
 User Mode Time
  Thread       Time
   1:358       0 days 0:00:47.408
   &lt;b&gt;2:150&lt;/b&gt;       0 days &lt;b&gt;0:00:06.859&lt;/b&gt;
   0:d8        0 days 0:00:00.000
&lt;/pre&gt;
&lt;p&gt;Now we should compare this output with the output from the previous run, and find the thread whose CPU time has increased the most. In the sample application, it definitely is the thread 0x150. Now we can attach Visual Studio debugger to the application, switch to this thread and check why it is spinning. 
&lt;h6&gt;&lt;a&gt;Debugging stack overflow &lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;CDB can also be very useful when we want to find the reason of a stack overflow exception. Of course, uncontrolled recursion is the most typical reason of stack overflows, and it is usually enough to look at the call stack of the offending thread to find the place where it went out of control. Visual Studio can do it just fine, so why use CDB? Lets think about more complicated cases. For example, what if our application contains an algorithm that relies on recursion? We put significant efforts into designing the algorithm and keeping recursion under control in all possible situations, but sometimes the stack still overflows. Why? Probably, because some functions used by the algorithm occupy too much space on the stack under certain conditions. How can we determine the amount of stack space occupied by a function? Unfortunately, Visual Studio debugger does not offer an easy way to do it. 
&lt;p&gt;It is also possible that an application raises a stack overflow exception even when the call stack does not show any signs of recursion. For example, take a look at &lt;a href="http://www.debuginfo.com/examples/src/StackOvfDemo.cpp"&gt;StackOvfDemo.cpp&lt;/a&gt; sample. If you compile it and run under debugger, stack overflow will soon occur. But the call stack at the moment of the exception looks innocent: &lt;pre&gt;StackOvfDemo.exe!_woutput
StackOvfDemo.exe!wprintf
StackOvfDemo.exe!ProcessStringW
StackOvfDemo.exe!ProcessStrings
StackOvfDemo.exe!main
StackOvfDemo.exe!mainCRTStartup
KERNEL32.DLL!_BaseProcessStart@4
&lt;/pre&gt;
&lt;p&gt;Obviously, one of the functions on the call stack is using too much stack space. But how can we find this function? Of course, with the help of CDB – its 'kf' command allows to display the number of bytes occupied by every function on the call stack. While the application is still stopped in Visual Studio debugger, lets run the following command: &lt;pre&gt;  cdb -pv -pn stackovfdemo.exe -logo out.txt -c &amp;quot;~*kf;q&amp;quot;
&lt;/pre&gt;
&lt;p&gt;(Be aware that by default 'kf' reports only the last 20 frames on the call stack, as we have already discussed in &lt;a href="http://www.debuginfo.com/#debugdeadlocks"&gt;Debugging Deadlocks&lt;/a&gt; section. If you want to display more than 20 frames, change ~*kf to, for example, ~*kf1000. Also note that ~*kf will report the call stacks of all threads. If the application contains lots of threads, it can be undesirable, and the command can be changed to '~~[tid]kf', where 'tid' is the thread id of the target thread (for example, '~~[0x3a8]kf')) 
&lt;p&gt;This command would display something like this: &lt;pre&gt;.  0  Id: 210.3a8 Suspend: 1 Teb: 7ffde000 Unfrozen
  Memory  ChildEBP RetAddr  
          00033440 0041aca5 StackOvfDemo!_woutput+0x22
       44 00033484 00415eed StackOvfDemo!wprintf+0x85
       d8 0003355c 00415cc5 StackOvfDemo!ProcessStringW+0x2d
    &lt;b&gt;fc878&lt;/b&gt; 0012fdd4 00415a44 &lt;b&gt;StackOvfDemo!ProcessStrings&lt;/b&gt;+0xe5
      108 0012fedc 0041c043 StackOvfDemo!main+0x64
       e4 0012ffc0 7c4e87f5 StackOvfDemo!mainCRTStartup+0x183
       30 0012fff0 00000000 KERNEL32!BaseProcessStart+0x3d
&lt;/pre&gt;
&lt;p&gt;Pay attention to the first column – it reports the number of bytes occupied by the corresponding function on the stack. Obviously, ProcessStrings function is using the lion's share of the available stack space, and is therefore responsible for stack overflow. 
&lt;p&gt;If you wonder why ProcessStrings function requires so much space on the stack, here is the explanation. This function uses ATL's A2W macro to convert strings from ANSI to Unicode, and this macro uses _alloca function internally to allocate memory on the stack. The memory allocated with _alloca is released only when its caller (ProcessStrings in this case) returns. Until ProcessStrings returns control, every subsequent call to A2W (and therefore _alloca) will allocate additional space on the stack, quickly exhausting it.&lt;br&gt;Bottom line: avoid using _alloca in a loop. &lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+windbg+the+easy+way+(part1)&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!898.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!898.entry</guid><pubDate>Tue, 19 Jun 2007 11:45:29 GMT</pubDate><slash:comments>1</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!898/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!898.entry#comment</wfw:comment><dcterms:modified>2007-06-19T11:52:10Z</dcterms:modified></item><item><title>JUnit入门(转贴)</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!689.entry</link><description>&lt;p&gt; &lt;p&gt;(转贴于&lt;a title="http://blog.csdn.net/ai92/archive/2005/02/26/302844.aspx" href="http://blog.csdn.net/ai92/archive/2005/02/26/302844.aspx"&gt;http://blog.csdn.net/ai92/archive/2005/02/26/302844.aspx&lt;/a&gt;) &lt;p&gt;PS: JUnit是一个好东西。搞Java的同学们，不要说你不晓得哈。 &lt;p&gt;一、简介 &lt;p&gt;JUnit是一个开源的java单元测试框架。在1997年，由 Erich Gamma 和 Kent Beck 开发完成。这两个牛人中 Erich Gamma 是 GOF 之一；Kent Beck 则在 XP 中有重要的贡献（你觉得眼熟一点都不奇怪）。 &lt;p&gt;正如常言道：“麻雀虽小，五脏俱全。” JUnit设计的非常小巧，但是功能却非常强大。 &lt;p&gt;下面是JUnit一些特性的总结： &lt;p&gt;1) 提供的API可以让你写出测试结果明确的可重用单元测试用例 &lt;p&gt;2) 提供了三种方式来显示你的测试结果，而且还可以扩展 &lt;p&gt;3) 提供了单元测试用例成批运行的功能 &lt;p&gt;4) 超轻量级而且使用简单，没有商业性的欺骗和无用的向导 &lt;p&gt;5) 整个框架设计良好，易扩展 &lt;p&gt;对不同性质的被测对象，如Class，Jsp，Servlet，Ejb等，Junit有不同的使用技巧。由于本文的性质，以下仅以Class测试为例。 &lt;p&gt;下面我们就叩开JUnit的大门吧！ &lt;p&gt;二、下载 &lt;p&gt;点击&lt;a href="http://www.junit.org/"&gt;http://www.junit.org&lt;/a&gt;可以下载到最新版本的JUnit，本文使用的为3.8.1版。至于安装或者配置之类，你只需要轻松的将下载下来的压缩包中的jar文件，放到你工程的classpath中就可以了。 &lt;p&gt;这样，你的系统中就可以使用JUnit编写单元测试代码了（是不是很简单）！ &lt;p&gt;三、HelloWorld &lt;p&gt;记得在几乎每本语言教学书上都能找到HelloWorld这个入门代码。今天在这里，我们也从一个简单到根本不用单元测试的例子入手。这是一个只会做两数加减的超级简单的计算器（小学一年级必备极品）。代码如下： &lt;p&gt;public class SampleCalculator &lt;p&gt;{ &lt;p&gt;public int add(int augend , int addend) &lt;p&gt;{ &lt;p&gt;return augend + addend ; &lt;p&gt;} &lt;p&gt;public int subtration(int minuend , int subtrahend) &lt;p&gt;{ &lt;p&gt;return minuend - subtrahend ; &lt;p&gt;} &lt;p&gt;} &lt;p&gt;将上面的代码编译通过。下面就是我为上面程序写的一个单元测试用例： &lt;p&gt;//请注意这个程序里面类名和方法名的特征 &lt;p&gt;public class TestSample extends TestCase &lt;p&gt;{ &lt;p&gt;public void testAdd() &lt;p&gt;{ &lt;p&gt;SampleCalculator calculator = new SampleCalculator(); &lt;p&gt;int result = calculator.add(50 , 20); &lt;p&gt;assertEquals(70 , result); &lt;p&gt;} &lt;p&gt;public void testSubtration() &lt;p&gt;{ &lt;p&gt;SampleCalculator calculator = new SampleCalculator(); &lt;p&gt;int result = calculator.subtration(50 , 20); &lt;p&gt;assertEquals(30 , result); &lt;p&gt;} &lt;p&gt;} &lt;p&gt;好了，在DOS命令行里面输入javac -classpath .;junit.jar TestSample.java 将测试类编译通过。然后再输入 java -classpath .;junit.jar junit.swingui.TestRunner TestSample 运行测试类，你会看到如下的窗口。 &lt;p&gt;&lt;img alt="" hspace=0 src="http://blog.csdn.net/images/blog_csdn_net/ai92/85479/r_TestRunner.jpg" align=baseline border=0&gt; &lt;p&gt;上图中，绿色说明单元测试通过，没有错误产生；如果是红色的，则就是说测试失败了。这样一个简单的单元测试就完成了，是不是很容易啊？ &lt;p&gt;按照框架规定：编写的所有测试类，必须继承自junit.framework.TestCase类；里面的测试方法，命名应该以Test开头，必须是public void 而且不能有参数；而且为了测试查错方便，尽量一个TestXXX方法对一个功能单一的方法进行测试；使用assertEquals等junit.framework.TestCase中的断言方法来判断测试结果正确与否。 &lt;p&gt;你可以对比着上面测试类中的实现来体会下规定——很简单！而且你在这个测试类中有加入多少个测试方法，就会运行多少个测试方法。 &lt;p&gt;四、向前一步 &lt;p&gt;学完了HelloWorld，你已经可以编写标准的单元测试用例了。但是还有一些细节，这里还要说明一下。不要急，很快的！ &lt;p&gt;你在看上面的代码的时候，是不是注意到每个TestXXX方法中都有一条SampleCalculator初始化语句？这很明显不符合编码规范。你可能正要将它提取出来放到构造函数里面去。且慢！在JUnit中的初始化是建议在Setup方法中作的。JUnit提供了一对方法，一个在运行测试方法前初始化一些必备条件而另一个就是测试完毕后去掉初始化的条件（见下图）。 &lt;p&gt;&lt;img alt="" hspace=0 src="http://blog.csdn.net/images/blog_csdn_net/ai92/85479/r_setupdown.jpg" align=baseline border=0&gt; &lt;p&gt;另外你是否注意到，上面弹出窗口的一个细节，在绿条下面有Errors、Failures统计。这两者有何区别呢？ &lt;p&gt;Failures作为单元测试所期望发生的错误，它预示你的代码有bug，不过也可能是你的单元测试代码有逻辑错误（注意是逻辑错误）。Errors不是你所期待的，发生了Error你可以按照下面的顺序来检查： &lt;p&gt;检查测试所需的环境，如：数据库连接 &lt;p&gt;检查单元测试代码 &lt;p&gt;检查你的系统代码 &lt;p&gt;五、成批运行test case &lt;p&gt;这是前面提到的JUnit特性之一。它方便于系统单元测试的成批运行。使用起来也是非常简单，先看下使用代码： &lt;p&gt;import junit.framework.Test; &lt;p&gt;import junit.framework.TestSuite; &lt;p&gt;public class TestAll{ &lt;p&gt;public static Test suite(){ &lt;p&gt;TestSuite suite = new TestSuite(&amp;quot;TestSuite Test&amp;quot;); &lt;p&gt;suite.addTestSuite( TestSample.class); &lt;p&gt;return suite; &lt;p&gt;} &lt;p&gt;} &lt;p&gt;这个测试程序的编译、运行，和上面TestSample的方式是一样的。 &lt;p&gt;javac -classpath .;junit.jar TestAll.java &lt;p&gt;java -classpath .;junit.jar junit.swingui.TestRunner TestAll &lt;p&gt;怎么样？这样你在suite方法里面添加几个TestCase就会运行几个，而且它也可以添加TestSuite来将小一点的集合加入大的集合中来，方便了对于不断增加的TestCase的管理和维护。 &lt;p&gt;呵呵，你觉得suite方法的作用是不是于java应用程序的main很类似？并且这里的suite必须严格遵守上面的写法！ &lt;p&gt;六、TestRunner &lt;p&gt;在JUnit中已经给出了三种方式表示的TestRunner。你可以分别运行体验下他们的不同。 &lt;p&gt;junit.swingui.TestRunner &lt;p&gt;junit.awtui.TestRunner &lt;p&gt;junit.textui.TestRunner &lt;p&gt;七、总结 &lt;p&gt;本文轻松简要的介绍了JUnit使用的入门知识。完全没有涉及到深入的技巧和使用规范。而这些请关注我关于JUnit高级使用、JUnit源代码分析的文章&lt;img src="http://c.services.spaces.live.com/CollectionWebService/c.gif?cid=-508484562895931526&amp;page=RSS%3a+JUnit%e5%85%a5%e9%97%a8(%e8%bd%ac%e8%b4%b4)&amp;referrer=" width="1px" height="1px" border="0" alt=""&gt;&lt;img style="position:absolute" alt="" width="0px" height="0px" src="http://c.live.com/c.gif?NC=31263&amp;amp;NA=1149&amp;amp;PI=73329&amp;amp;RF=&amp;amp;DI=3919&amp;amp;PS=85545&amp;amp;TP=tangl99.spaces.live.com&amp;amp;GT1=tangl99"&gt;</description><comments>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!689.entry#comment</comments><guid isPermaLink="true">http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!689.entry</guid><pubDate>Tue, 20 Mar 2007 07:58:26 GMT</pubDate><slash:comments>0</slash:comments><msn:type>blogentry</msn:type><live:type>blogentry</live:type><live:typelabel>Blog entry</live:typelabel><wfw:commentRss>http://tangl99.spaces.live.com/blog/cns!F8F17FFBEED1777A!689/comments/feed.rss</wfw:commentRss><wfw:comment>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!689.entry#comment</wfw:comment><dcterms:modified>2007-03-20T07:58:26Z</dcterms:modified></item><item><title>ACM程序设计大赛校内选拔赛初赛的一道题目:Spanning trees in a fan</title><link>http://tangl99.spaces.live.com/Blog/cns!F8F17FFBEED1777A!426.entry</link><description>&lt;div&gt;
&lt;h2&gt;Description&lt;/h2&gt;
&lt;p&gt;A fan of order &lt;em&gt;n&lt;/em&gt; is a graph on the vertices { 0, 1, ..., n } with &lt;em&gt;2n-1&lt;/em&gt; edges defined as follows:&lt;br&gt;Vertex 0 is connected by an edge to each of the other n vertices, and vertex k is connected by an edge to vertex k+1, for &lt;em&gt;1&amp;lt;=k&amp;lt;n. &lt;/em&gt;Here, for example, is the fan of order 3, which has four vertices and five edges.&lt;br&gt;&lt;img alt="" src="http://acm.scu.edu.cn/soj/problem/2484/image1.jpg"&gt; 
&lt;p&gt;The problem is: how many spanning trees are in such a graph?&lt;br&gt;A spanning tree is a subgraph containing all the vertices, and containing enough edges to make the subgraph connected yet not so many that it has a cycle.&lt;br&gt;The following shows all the spanning trees of fan of order 2.&lt;br&gt;&lt;img alt="" src="http://acm.scu.edu.cn/soj/problem/2484/image2.jpg"&gt; 
&lt;h2&gt;Input&lt;/h2&gt;
&lt;p&gt;The input contains multiple test cases.&lt;br&gt;For each test case, there is one integer &lt;em&gt;n (1&amp;lt;=n&amp;lt;=10000)&lt;/em&gt; in a line, indicate a fan of order &lt;em&gt;n&lt;/em&gt;&lt;br&gt;A line with a single zero ends the input and should not be processed. 
&lt;h2&gt;Output&lt;/h2&gt;
&lt;p&gt;The number of different spanning tree in the fan, moduled by 10000. 
&lt;h2&gt;Sample Input&lt;/h2&gt;&lt;pre&gt;1
2
3
4
0
&lt;/pre&gt;
&lt;h2&gt;Sample Output&lt;/h2&gt;&lt;pre&gt;1
3
8
21
&lt;/pre&gt;
&lt;p&gt; 这道习题是川大ACM程序设计大赛中的初试的一道题目，可以在&lt;a href="http://acm.scu.edu.cn/soj/problem.action?id=2484"&gt;&lt;u&gt;&lt;font color="#800080"&gt;http://acm.scu.edu.cn/soj/problem.action?id=2484&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;上找到。
&lt;h1&gt;我的解题思路:&lt;/h1&gt;
&lt;p&gt;  针对Spanning tree的特点，可以发现，其实这个问题是递归的，既是可以通过子spanning trees来构成一个大的spanning trees。
&lt;p&gt;   n+1个节点的spanning tree在节点(1,2,3,...,n}上，可以把{1,2,3,...,n}分成m个组，每个组内的节点编号是连续的，但是个数可以不相同。比如，(1,2,3),(4,5,6,7),(8,9),(10,...),...等等组。可以发现这个n+1个节点的spanning trees完全可以由{1,2,3},{4,5,6,7},{8,9},{10,...},...这些子的spanning trees构成，这些子spanning trees的底边是连通的。由于不能有环，又由于他们的底边是连通的，那么(1,2,3),(4,5,6,7)这些组中只能取某一个节点与0节点连接，构成子spanning trees。那么，k个节点的组可以有k个不同取法，也就是说，可以构成k个spanning tree。那么n+1个节点的spanning trees的个数就是每个子spanning trees的个数乘以子spanning trees的组合方式的情况个数。
&lt;p&gt;   下面的问题就变成一个简单的数的排列组合问题了。既是{1,2,3,...,n}可以分成多少个组，要求组内的数字是连续的。这个问题也可以通过递归来解决。
&lt;p&gt;   我是这样来看待分组的分配的：&lt;br&gt;  (1),{2,3,..n}&lt;br&gt;  (1,2),{3,4,...,n}&lt;br&gt;  (1,2,3),{4,5,..,n}&lt;br&gt;  ...&lt;br&gt;  (1,2,3,...,n-1),{n}&lt;br&gt;  (1,2,3,4,...,n)&lt;br&gt;  可能你会说，为什么都是只考虑两个分组？注意了，这里列出的并非是分组的情况。小括号才是分组的括号，而大括号是分组的所有组合的集合，比如(1,2),(3),(4,5,...,n)其实就包含在了(1,2),{3,4,..,n}中了，因为{3,4,...,n}集合里面就包含了(3),(4,5,...,n)这种分组了。
&lt;p&gt;  一个k个节点的组，必须取且只能取一个点与0节点连接，就形成一个子spanning tree，所以k个节点的组有k中spanning tree的构成方式。
&lt;p&gt;  既然发现了问题是递归的，那么我们可以来构造递归式了。如果n+1个节点的spanning trees解为S(n)，根据上面的分组的分配方式，可以看到每种分配方式对应的解的个数
&lt;p&gt;  (1),{2,3,..n}                          1*S(n-1)&lt;br&gt;  (1,2),{3,4,...,n}                     2*S(n-2)&lt;br&gt;  (1,2,3),{4,5,..,n}                   3*S(n-3)&lt;br&gt;  ...                                            ...&lt;br&gt;  (1,2,3,...,n-1),{n}                  (n-1)*S(1)&lt;br&gt;  (1,2,3,...,n)                           n&lt;br&gt;
&lt;p&gt;则可以列出下面的公式：
&lt;p&gt;  S(n) = S(n-1) + 2*S(n-2) + 3*S(n-3) + 4*S(n-4) + ... + (n-1)*S(1) + n
&lt;p&gt;  S(1) = 1
&lt;p&gt;有了这样的公式，直接写递归函数，就可以很容易求出这道题目的解，但是仅仅这样是不够的。ACM的题目大部分都设置了算法运行时间的限制，这个递归式还有点复杂，其实还可以进一步简化的。
&lt;p&gt;下面是简化的推导：
&lt;p&gt;  S(n-1) = S(n-2) + 2*S(n-3) + ... + (n-2)*S(1) + n-1&lt;br&gt;=&amp;gt;  S(n) - S(n-1) = S(n-1) + S(n-2) + S(n-3) + ... + 1&lt;br&gt;=&amp;gt;  S(n) = 2*S(n-1) + S(n-2) + S(n-3) + ... +1&lt;br&gt;=&amp;gt;  S(n-1) = 2*S(n-2) + S(n-3) + ... + 1&lt;br&gt;=&amp;gt;  S(n) - S(n-1) = 2*S(n-1) - S(n-2)&lt;br&gt;=&amp;gt;  S(n) = 3*S(n-1) - S(n-2)
&lt;p&gt;最后得到一个简单的递归式： S(n) = 3*S(n-1) - S(n-2)&lt;br&gt;
&lt;p&gt;利用这个递归式写出的程序，已经可以通过ACM的Online Judge的Accpeted了。但是还有更简单的算法没？或许我们应该把S(n)的通项公式求出来，说不定就能得到一个O(1)的算法。
&lt;p&gt;根据上面的递归式求S(n)的通项公式的方法在中学数学里面已经学过了，任何一个上述递归式，都可以通过特征根求解的方式求出通项公式出来，至于特征根方程的构造，这里有个网址上面有介绍：&lt;a href="http://202.113.96.26/discrete/xxwb/tj/chap7.htm"&gt;&lt;u&gt;&lt;font color="#800080"&gt;http://202.113.96.26/discrete/xxwb/tj/chap7.htm&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;
&lt;p&gt; 
&lt;h1&gt;我的解题程序：&lt;/h1&gt;
&lt;p&gt; &lt;font color="#008080"&gt;1&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;/font&gt;&lt;span style="color:#008000"&gt;//&lt;/span&gt;&lt;span style="color:#008000"&gt; SOJ2484.cpp : 定义控制台应用程序的入口点。&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;2&lt;/span&gt;&lt;span style="color:#008000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;/span&gt;&lt;span style="color:#008000"&gt;//&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;3&lt;/span&gt;&lt;span style="color:#008000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;4&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;#include &lt;/span&gt;&lt;span style="color:#000000"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#000000"&gt;stdio.h&lt;/span&gt;&lt;span style="color:#000000"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;5&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;#include &lt;/span&gt;&lt;span style="color:#000000"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#000000"&gt;stdlib.h&lt;/span&gt;&lt;span style="color:#000000"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;6&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;#include &lt;/span&gt;&lt;span style="color:#000000"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#0000ff"&gt;string&lt;/span&gt;&lt;span style="color:#000000"&gt;.h&lt;/span&gt;&lt;span style="color:#000000"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;7&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;8&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;/span&gt;&lt;span style="color:#0000ff"&gt;#define&lt;/span&gt;&lt;span style="color:#000000"&gt; MAX_NUM_S 10000&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;9&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;/span&gt;&lt;span style="color:#0000ff"&gt;#define&lt;/span&gt;&lt;span style="color:#000000"&gt; MOD 10000&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;10&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;11&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;/span&gt;&lt;span style="color:#0000ff"&gt;long&lt;/span&gt;&lt;span style="color:#000000"&gt; g_S[MAX_NUM_S];&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;12&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;13&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top&gt;&lt;img style="display:none" alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/ContractedBlock.gif" align=top&gt;&lt;/span&gt;&lt;span style="border-right:#808080 1px solid;border-top:#808080 1px solid;display:none;border-left:#808080 1px solid;border-bottom:#808080 1px solid;background-color:#ffffff"&gt;/**/&lt;/span&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;/*&lt;/span&gt;&lt;span style="color:#008000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;14&lt;/span&gt;&lt;span style="color:#008000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/InBlock.gif" align=top&gt;S(n) = 3*S(n-1) - S(n-2)&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;15&lt;/span&gt;&lt;span style="color:#008000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top&gt;&lt;/span&gt;&lt;span style="color:#008000"&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;16&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/None.gif" align=top&gt;&lt;/span&gt;&lt;span style="color:#0000ff"&gt;int&lt;/span&gt;&lt;span style="color:#000000"&gt; S(&lt;/span&gt;&lt;span style="color:#0000ff"&gt;int&lt;/span&gt;&lt;span style="color:#000000"&gt; n)&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;17&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top&gt;&lt;img style="display:none" alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/ContractedBlock.gif" align=top&gt;&lt;/span&gt;&lt;span style="border-right:#808080 1px solid;border-top:#808080 1px solid;display:none;border-left:#808080 1px solid;border-bottom:#808080 1px solid;background-color:#ffffff"&gt;...&lt;/span&gt;&lt;span&gt;&lt;span style="color:#000000"&gt;{&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;18&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/InBlock.gif" align=top&gt;    &lt;/span&gt;&lt;span style="color:#0000ff"&gt;if&lt;/span&gt;&lt;span style="color:#000000"&gt;(n &lt;/span&gt;&lt;span style="color:#000000"&gt;==&lt;/span&gt;&lt;span style="color:#000000"&gt; &lt;/span&gt;&lt;span style="color:#000000"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;)&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;19&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/InBlock.gif" align=top&gt;        &lt;/span&gt;&lt;span style="color:#0000ff"&gt;return&lt;/span&gt;&lt;span style="color:#000000"&gt; &lt;/span&gt;&lt;span style="color:#000000"&gt;1&lt;/span&gt;&lt;span style="color:#000000"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;20&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/InBlock.gif" align=top&gt;    &lt;/span&gt;&lt;span style="color:#0000ff"&gt;else&lt;/span&gt;&lt;span style="color:#000000"&gt; &lt;/span&gt;&lt;span style="color:#0000ff"&gt;if&lt;/span&gt;&lt;span style="color:#000000"&gt;(n &lt;/span&gt;&lt;span style="color:#000000"&gt;==&lt;/span&gt;&lt;span style="color:#000000"&gt; &lt;/span&gt;&lt;span style="color:#000000"&gt;2&lt;/span&gt;&lt;span style="color:#000000"&gt;)&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;21&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/InBlock.gif" align=top&gt;        &lt;/span&gt;&lt;span style="color:#0000ff"&gt;return&lt;/span&gt;&lt;span style="color:#000000"&gt; &lt;/span&gt;&lt;span style="color:#000000"&gt;3&lt;/span&gt;&lt;span style="color:#000000"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;22&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/InBlock.gif" align=top&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:#008080"&gt;23&lt;/span&gt;&lt;span style="color:#000000"&gt;&lt;img alt="" src="http://www.actiprosoftware.com/Images/OutliningIndicators/InBlock.gif" align=top&gt;    &lt;/span&gt;&lt;span style="color:#0000ff"&gt;if&lt;/span&gt;&lt;span style="color:#000000"&gt;(g_S[n] &lt;/span&gt;&lt;span style="color:#000000"&gt;!=&lt;/span&gt;&lt;span style="color:#000000"&gt; &lt;/span&gt;&lt;span style="color:#000000"&gt;0&lt;/span&gt;&