??xml version="1.0" encoding="utf-8" standalone="yes"?>国产精品久久久久9999高清,精品熟女少妇aⅴ免费久久,丁香五月综合久久激情http://m.shnenglu.com/lauer3912/category/17748.html没有理由不学?/description>zh-cnSat, 19 Jul 2014 15:01:43 GMTSat, 19 Jul 2014 15:01:43 GMT60Google Breakpad 完全解析Q二Q?—?Windows前台实现?/title><link>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159785.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 07 Nov 2011 13:36:00 GMT</pubDate><guid>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159785.html</guid><wfw:comment>http://m.shnenglu.com/lauer3912/comments/159785.html</wfw:comment><comments>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159785.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/lauer3912/comments/commentRss/159785.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/lauer3912/services/trackbacks/159785.html</trackback:ping><description><![CDATA[<div><h2>Google Breakpad 完全解析Q二Q?—— Windows前台实现?/h2> 2011q?2??— Asp J <div><h3>Table of contents for Google Breakpad 完全解析</h3><ol><li><a title="Google Breakpad 完全解析Q一Q?—— Windows入门?>Google Breakpad 完全解析Q一Q?—— Windows入门?/a></li><li>Google Breakpad 完全解析Q二Q?—— Windows前台实现?/li></ol></div> <p>原创文章Q{载请标明出处QSoul Apogee (<a target="_blank">http://bigasp.com</a>)Q谢谢?/p> <p>好,看完?a target="_blank">如何使用breakpad</a>Q我们现在看看breakpad在Windows下到底是如何实现的呢Q?/p> <h3><strong>代码l构</strong></h3> <p>在我们来看breakpad是如何实现其强大的功能之前,我们先来看一下他的代码结构吧?/p> <p>Google breakpad的源代码都在src的目录下Q他分ؓ如下几个文g夹:<br /> clientQ这下面包含了前台应用程序中捕捉dump的部分代码,里面按照q_分成各个子文件夹<br /> commonQ前台后台都会用到的部分基础代码Q字W串转换Q内存读写,md5马?br /> google_breakpadQbreakpad中公q头文?br /> processorQ用于在后台处理崩溃的核心代?br /> testingQ测试工E?br /> third_partyQ第三方?br /> toolsQ一些小工具Q用于处理dump文g和符可</p> <p>我们先来看Windows下前台实现的部分Q也是client文g夹下的代码?/p> <h3><strong>breakpad的崩溃捕h?/strong></h3> <p>在Windows下捕获崩溃,大家很容易会惛_那个捕获l构化异常的ApiQ?a target="blank" >SetUnhandledExceptionFilter</a>?/p> <p>breakpad中也使用了这个Api来实现的崩溃捕获Q另外,breakpadq捕获了另外两种C++q行库提供的崩溃Q一U是使用<a target="blank" >_set_purecall_handler</a>捕获U虚函数调用产生的崩溃,q有一U是使用<a target="blank" >_set_invalid_parameter_handler</a>捕获错误的参数调用生的崩溃?/p> <div><div class="bfxhtbl" id="highlighter_32943" cpp"=""><div><a command_help="" help"="">?</a></div><table cellpadding="0" cellspacing="0" border="0"><tbody><tr><td><div number1="" index0="" alt2"="">1</div><div number2="" index1="" alt1"="">2</div><div number3="" index2="" alt2"="">3</div><div number4="" index3="" alt1"="">4</div><div number5="" index4="" alt2"="">5</div><div number6="" index5="" alt1"="">6</div><div number7="" index6="" alt2"="">7</div><div number8="" index7="" alt1"="">8</div><div number9="" index8="" alt2"="">9</div><div number10="" index9="" alt1"="">10</div></td><td><div><div number1="" index0="" alt2"=""><code spaces"="">    </code><code keyword="" bold"="">if</code> <code plain"="">(handler_types & HANDLER_EXCEPTION)</code></div><div number2="" index1="" alt1"=""><code spaces"="">      </code><code plain"="">previous_filter_ = SetUnhandledExceptionFilter(HandleException);</code></div><div number3="" index2="" alt2"=""> </div><div number4="" index3="" alt1"=""><code preprocessor"="">#if _MSC_VER >= 1400  // MSVC 2005/8</code></div><div number5="" index4="" alt2"=""><code spaces"="">    </code><code keyword="" bold"="">if</code> <code plain"="">(handler_types & HANDLER_INVALID_PARAMETER)</code></div><div number6="" index5="" alt1"=""><code spaces"="">      </code><code plain"="">previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);</code></div><div number7="" index6="" alt2"=""><code preprocessor"="">#endif  // _MSC_VER >= 1400</code></div><div number8="" index7="" alt1"=""> </div><div number9="" index8="" alt2"=""><code spaces"="">    </code><code keyword="" bold"="">if</code> <code plain"="">(handler_types & HANDLER_PURECALL)</code></div><div number10="" index9="" alt1"=""><code spaces"="">      </code><code plain"="">previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);</code></div></div></td></tr></tbody></table></div></div> <p>另外׃C++q行库提供的崩溃回调中,q不会提供当前的U程现场和崩溃信息,所以breakpad会自q成好q些信息Q然后请求生成dump?br /> q里值得一说的是,在非异常崩溃处理中,breakpad获取U程现场使用的函数是RtlCaptureContext而不是GetThreadContext?br /> RtlCaptureContext只能捕获当前U程的现场,而GetThreadContext可以捕获LU程的现场,只要有这个线E的句柄卛_?br /> 但是GetThreadContext有两个不好的地方Q不能获取当前线E的现场Q获取现场前必须先用SuspendThread暂停目标U程?br /> 而RtlCaptureContext虽然只能获取当前U程的现场,但是调用他时可以不用暂停U程的运行?br /> 对于breakpad来说Q崩溃发生后早获取现场p好,所以breakpad使用RtlCaptureContext函数作ؓ他的U程获取函数?/p> <h3><strong>breakpad中的C/Sl构</strong></h3> <p>׃breakpad是在q程外抓取dumpQ所以breakpad需要实C个C/Sl构来处理崩溃进E抓取dump的请求?/p> <p><strong>1. breakpad跨进E通信的实?br /> </strong>breakpad中用了命名道来实现IPC?/p> <p>在客LQ初始化ExceptionHandler的时候,如果指定了PipeNameQ也pC此旉要用进E外的dump?取,ExceptionHandlerQ会建立一?nbsp;CrashGenerationClient的对象,p个对象连接服务端Q将自己注册到服务端?厅R?br /> 大家可以参看exception_handler.cc中的ExceptionHandler::Initialize函数?/p> <p>在服务端Q初始化CrashGenerationServer的时候,׃建立一个命名管道,q等待客L来连接。一旦有客户端连接上来,服务端会 为每一个客L生成一个ClientInfo的对象,之后用这个对象来理所有的客户端,一旦有崩溃发生Q服务端都会从这个对象中取出dump所需要的?息?br /> 大家可以参看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函数?/p> <p><strong>2. breakpad捕获崩溃生成dump的流E?br /> </strong>breakpadq程外生成dump的流E大概如下:<br /> <strong>google-breakpad-out-of-process-dump:</strong><br /><a target="_blank"><img src="http://bigasp.com/wp-content/uploads/downloads/thumbnails/2011/02/google-breakpad-out-of-process-dump.jpg" alt="google-breakpad-out-of-process-dump" style="max-width:500px;" /></a><br /> q段程的代码就是crash_generation_client.cc和crash_generation_server.cc?/p> <p>有两个简单的问题Q这里说明一下,高手们就L接忽略吧Q咩哈哈Q?br /> <strong>在服务端如何为客L生成事g句柄Q?/strong><br /> 使用DuplicateHandleQ即可把L一个内核对象的句柄复制到其他进E,q且可以指定产生的句柄的权限?/p> <p><strong>如何异步的等待一个事Ӟ<br /> </strong>使用RegisterWaitForSingleObjectQ即可异步的{待一个事Ӟ当事件发生的时候,可以回调到一个指定的?调函CQ但是要注意的是QRegisterWaitForSingleObject会在一个新的线E中来等待这个事Ӟ此处很容易生多U程的调用,需 要注意线E问题?/p> <p><strong>3. 服务端关键数据结构:ClientInfo<br /> </strong>ClientInfo是服务端中最重要的数据结构,服务端通过它来理所有的客户端。客L注册Ӟ会保存或生成里面所有的信息Q在客户端请求生成dump的时候,服务端就会通过ClientInfo获取所有客L的信息。ClientInfo中保存了如下信息Q?/p> <ul><li>客户端进Epid和句?/li><li>生成Minidump的类?/li><li>自定义的客户端信?/li><li>客户端崩溃的U程ID</li><li>客户端崩溃的信息</li><li>客户端请求崩溃所使用的事件句?/li></ul> <p>q里有一个问题:在客L发生崩溃Ӟ服务器如何通过ClientInfo获取到客L的崩溃信息呢Q?/p> <p>客户端中有几个用于保存崩溃信息的变量Q在注册Ӟ客户端会这几个变量的地址发送至服务端,服务端将其保存在ClientInfo中,然后当崩?发生的时候,服务端就可以通过ReadProcessMemoryd客户端中的信息,从而生成dump。这样做避免了每次发生崩溃Q都要通过Pipe 崩溃信息传递到服务端中M?/p> <p>q些变量分别是:崩溃的线EIDQEXCEPTION_POINTERS和MDRawAssertionInfo?br /> EXCEPTION_POINTERS和MDRawAssertionInfo的区别在于,异常崩溃的信息会被写入EXCEPTION_POINTERSQ非异常崩溃Q非法参数和U虚函数调用Q的信息会被写入MDRawAssertionInfo中?/p> <h3><strong>dump文g的上?/strong></h3> <p>在breakpad的工E中Q有一个工E叫做:crash_report_senderQ里面是一个上传崩溃文件的c,他的实现很简单,他用Windows Internet Api来完成dump文g的上传?br /> 在用crash_report_senderӞ可以为其指定一个checkpoint_file?/p> <div><div class="lrrflph" id="highlighter_216810" cpp"=""><div><a command_help="" help"="">?</a></div><table cellpadding="0" cellspacing="0" border="0"><tbody><tr><td><div number1="" index0="" alt2"="">1</div></td><td><div><div number1="" index0="" alt2"=""><code keyword="" bold"="">explicit</code> <code plain"="">CrashReportSender(</code><code keyword="" bold"="">const</code> <code plain"="">wstring &checkpoint_file);</code></div></div></td></tr></tbody></table></div></div> <p>q个文g只有一个作用,是用来保存上次上传崩溃的时间和今天上传q的崩溃的次数。通过q个文gQ我们就可以来设|每日上传的崩溃的最大数量?/p> <div><div class="hdrfzzz" id="highlighter_535960" cpp"=""><div><a command_help="" help"="">?</a></div><table cellpadding="0" cellspacing="0" border="0"><tbody><tr><td><div number1="" index0="" alt2"="">1</div><div number2="" index1="" alt1"="">2</div><div number3="" index2="" alt2"="">3</div><div number4="" index3="" alt1"="">4</div><div number5="" index4="" alt2"="">5</div><div number6="" index5="" alt1"="">6</div><div number7="" index6="" alt2"="">7</div><div number8="" index7="" alt1"="">8</div><div number9="" index8="" alt2"="">9</div><div number10="" index9="" alt1"="">10</div><div number11="" index10="" alt2"="">11</div><div number12="" index11="" alt1"="">12</div><div number13="" index12="" alt2"="">13</div><div number14="" index13="" alt1"="">14</div><div number15="" index14="" alt2"="">15</div><div number16="" index15="" alt1"="">16</div><div number17="" index16="" alt2"="">17</div><div number18="" index17="" alt1"="">18</div><div number19="" index18="" alt2"="">19</div><div number20="" index19="" alt1"="">20</div><div number21="" index20="" alt2"="">21</div><div number22="" index21="" alt1"="">22</div><div number23="" index22="" alt2"="">23</div><div number24="" index23="" alt1"="">24</div></td><td><div><div number1="" index0="" alt2"=""><code plain"="">CrashReportSender::CrashReportSender(</code><code keyword="" bold"="">const</code> <code plain"="">wstring &checkpoint_file)</code></div><div number2="" index1="" alt1"=""><code spaces"="">    </code><code plain"="">: checkpoint_file_(checkpoint_file),</code></div><div number3="" index2="" alt2"=""><code spaces"="">      </code><code plain"="">max_reports_per_day_(-1),</code></div><div number4="" index3="" alt1"=""><code spaces"="">      </code><code plain"="">last_sent_date_(-1),</code></div><div number5="" index4="" alt2"=""><code spaces"="">      </code><code plain"="">reports_sent_(0) {</code></div><div number6="" index5="" alt1"=""><code spaces"="">  </code><code color1="" bold"="">FILE</code> <code plain"="">*fd;</code></div><div number7="" index6="" alt2"=""><code spaces"="">  </code><code keyword="" bold"="">if</code> <code plain"="">(OpenCheckpointFile(L</code><code string"="">"r"</code><code plain"="">, &fd) == 0) {</code></div><div number8="" index7="" alt1"=""><code spaces"="">    </code><code plain"="">ReadCheckpoint(fd);</code></div><div number9="" index8="" alt2"=""><code spaces"="">    </code><code functions="" bold"="">fclose</code><code plain"="">(fd);</code></div><div number10="" index9="" alt1"=""><code spaces"="">  </code><code plain"="">}</code></div><div number11="" index10="" alt2"=""><code plain"="">}</code></div><div number12="" index11="" alt1"=""> </div><div number13="" index12="" alt2"=""><code plain"="">ReportResult CrashReportSender::SendCrashReport(</code></div><div number14="" index13="" alt1"=""><code spaces"="">    </code><code keyword="" bold"="">const</code> <code plain"="">wstring &url, </code><code keyword="" bold"="">const</code> <code plain"="">map<wstring, wstring> &parameters,</code></div><div number15="" index14="" alt2"=""><code spaces"="">    </code><code keyword="" bold"="">const</code> <code plain"="">wstring &dump_file_name, wstring *report_code) {</code></div><div number16="" index15="" alt1"=""><code spaces"="">  </code><code color1="" bold"="">int</code> <code plain"="">today = GetCurrentDate();</code></div><div number17="" index16="" alt2"=""><code spaces"="">  </code><code keyword="" bold"="">if</code> <code plain"="">(today == last_sent_date_ &&</code></div><div number18="" index17="" alt1"=""><code spaces"="">      </code><code plain"="">max_reports_per_day_ != -1 &&</code></div><div number19="" index18="" alt2"=""><code spaces"="">      </code><code plain"="">reports_sent_ >= max_reports_per_day_) {</code></div><div number20="" index19="" alt1"=""><code spaces"="">    </code><code keyword="" bold"="">return</code> <code plain"="">RESULT_THROTTLED;</code></div><div number21="" index20="" alt2"=""><code spaces"="">  </code><code plain"="">}</code></div><div number22="" index21="" alt1"=""> </div><div number23="" index22="" alt2"=""><code spaces"="">  </code><code comments"="">// 上传文g部分代码Q省?/code></div><div number24="" index23="" alt1"=""><code plain"="">}</code></div></div></td></tr></tbody></table></div></div> <p>调整每日上传崩溃的最大数量的函数是set_max_reports_per_day?/p> <p>需要注意的是:在上传dump文g的时候,crash_report_senderq不会对dump文gq行分析Q而是直接上传整个dump文gQ?如果你需要上传的dump文g非常大的话,可以考虑把崩溃分析处理的逻辑攑օ前台Q通过去重或者直接上传分析结果,减少上传的文件大?/p> <h3><strong>breakpad存在的问?/strong></h3> <h3><span style="font-weight: normal; font-size: 13px;">q程外生成dump有很多好处,其中最大的好处是不会被崩溃进E媄响,q样dump的过E就不容易出错,但是q样也有一定的弊端?/span></h3> <p><strong>1. 部分崩溃无法抓取<br /> </strong>在一些极端的崩溃Q如堆栈溢出之类的崩溃,q程外抓取dump有时候会p|?/p> <p><strong>2. 无法抓取死锁或者其他原因导致的q程僉|<br /> </strong>breakpad现在没有进E死锁的代码Q也没有在服务端控制客户端请求dump的代码,所以现在breakpad无法抓取死锁{进E僵ȝ问题。不q因为breakpad的定位是处理崩溃Q如果有q种需要的童鞋Q可以自行修改breakpad的代码,dq些功能?/p> <p><strong>3. Ҏ务端有依?br /> </strong>如果指定了在使用q程外抓取dumpQbreakpadҎ务端有依赖。主要体现在抓取dumpӞ如果服务端不存在Q客L无法正常抓取dumpQ甚x时会出现d?/p> <p>当然对于q些问题Q随着breakpad的发展肯定会来完善。如果,你遇C了这些问题,而又l过不了Q那改代码Qƈ且提交给breakpad吧,开源项目就是这么发展的?/p> <p>好,到此breakpad的Windows实现已l说完了Q如果有马问题Q还请多多指教。谢谢大家?/p><p style="margin-bottom:0pt; margin-top:0pt; "><a ><span style=" color:#0000ff; text-decoration:underline ;font-family:'宋体'; ">北京徯门中医院</span></a><a ><span style=" color:#0000ff; text-decoration:underline ;font-family:'宋体'; ">http://www.0531jsk.com/</span></a><span style=" font-size:10.5000pt; font-family:'宋体'; ">徯门中医院</span></p></div><img src ="http://m.shnenglu.com/lauer3912/aggbug/159785.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/lauer3912/" target="_blank">RTY</a> 2011-11-07 21:36 <a href="http://m.shnenglu.com/lauer3912/archive/2011/11/07/159785.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google Breakpad 完全解析Q一Q?—?Windows入门?/title><link>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159784.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 07 Nov 2011 13:34:00 GMT</pubDate><guid>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159784.html</guid><wfw:comment>http://m.shnenglu.com/lauer3912/comments/159784.html</wfw:comment><comments>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159784.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/lauer3912/comments/commentRss/159784.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/lauer3912/services/trackbacks/159784.html</trackback:ping><description><![CDATA[<div><h2>Google Breakpad 完全解析Q一Q?—— Windows入门?/h2> 2011q?1?3?— Asp J <div><h3>Table of contents for Google Breakpad 完全解析</h3><ol><li>Google Breakpad 完全解析Q一Q?—— Windows入门?/li><li><a title="Google Breakpad 完全解析Q二Q?—— Windows前台实现?>Google Breakpad 完全解析Q二Q?—— Windows前台实现?/a></li></ol></div> <p>原创文章Q{载请标明出处QSoul Apogee (<a target="_blank">http://bigasp.com</a>)Q谢谢? </p> <p><a title="Google Breakpad" target="_blank">Google breakpad</a>?一个非常实用的跨^台的崩溃转储和分析模块,他支持WindowsQLinux和Mac和Solaris。由于他本n跨^収ͼ所以很大的减少我们在^台移 植时的工作,毕竟崩溃转储Q每个^C都不同,使用h很难l一Q而Google breakpad帮我们做到了这一点,不管是哪个^C的崩溃,都能够进行统一的分析。现在很多工E都在用他Q最著名的几个如 ChromeQFirefoxQPicasa和Google Earth。另外他的License是BSD的,也就是说Q我们即便是在商业Y件中使用Q也是合法的Q哈哈,q么好的东西Q我们能放过么?现在p我们?看看q个奇的Y件吧?/p> <h3><strong>原理?/strong></h3> <p>breakpad抓取dump的方式和一般我们抓取dump的方式不一栗在breakpad的wiki上有一q图可以很好的概括他的原理?/p> <p><img title="breakpad" src="http://google-breakpad.googlecode.com/svn/wiki/breakpad.png" alt="" height="464" width="425" /></p> <p>breakpad把应用程序分成三个部分,代码Qbreakpad客户端和调试信息?/p> <p>1. 在build system中,通过symbol dumper用^台相关的调试信息生成q_无关的symbol文g。这样做的好处很明显Q一旦^台无关了Q所有^台的崩溃可以做l一的分析了?br /> 2. breakpad采取q程外{储和分析崩溃的方式,他用C/Sl构Q客L用来捕获当前q程中发生的崩溃Qƈ通知服务端崩溃发生。服务端用来响应客户端,抓取dump文g。这样做的目的是Z减少崩溃q程对dump的媄响?br /> 3. Dump生成后{发到崩溃分析器中Q这个部分可以在本地也可以在服务器上Q他对Dump文gq行解析Q生成可ȝ堆栈信息?/p> <p>q就是breakpad处理dump大概的流E?/p> <p>对于原理的介lgoogle写的已经相当好了。更多的详细信息Q可以直接移步到<a target="_blank">breakpad的wiki</a>?/p> <h3><strong>安装和编?/strong></h3> <p>breakpad的编译比较曲折,所以在此记录一下?/p> <p>~译breakpadQ请认你的机器上装有以下的软gQ?br /> 1. <a target="_blank">python 2.4.3</a><a target="_blank"><br /> </a>请不要用python3Q会报错。另外python2中推荐这个版本,使用新的版本在编译其他google的工E时有时会报?/p> <p>2. <a target="_blank">Windows SDK 7</a><a target="_blank"><br /> </a>如果没有q个Q编译会报错。另外这个是在线安装Q时间很久,最好ƈ行做其他的事情?/p> <p>3. VS2005的补?br /> KB918559<br /> KB926601<br /> KB935225<br /> KB943969<br /> KB947315</p> <p>已经安装了以上Y件的童鞋Q就可以开始进行下面的工作?/p> <p>1. 使用svn把代?a target="_blank">checkout</a>下来</p> <div><div class="nnjnzpp" id="highlighter_301422" shell"=""><div><a command_help="" help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0="" alt2"="">1</div><div number2="" index1="" alt1"="">2</div></td><td><div><div number1="" index0="" alt2"=""><code comments"=""># Non-members may check out a read-only working copy anonymously over HTTP.</code></div><div number2="" index1="" alt1"=""><code plain"="">svn checkout http:</code><code plain"="">//google-breakpad</code><code plain"="">.googlecode.com</code><code plain"="">/svn/trunk/</code> <code plain"="">google-breakpad-</code><code functions"="">read</code><code plain"="">-only</code></div></div></td></tr></tbody></table></div></div> <p>2. 讄Windows SDK 7<br /> 装过其他版本Windows SDK的童鞋,记得一定要q行q一步,SDK的安装程序,q不会帮你设|VS?br /> q行开始菜?>E序->Microsoft Windows SDK v7.0->Visual Studio Registration->Windows SDK Configuration ToolQ选择v7.0Q点击Make Current?/p> <p>3. 为python讄环境变量<br /> ׃breakpad使用python来生成Windows下的工程文gQ所以需要将python所在目录,讄到环境变量PATH中去?/p> <p>4. 生成Windows工程文g</p> <div><div class="pzvrddl" id="highlighter_825083" shell"=""><div><a command_help="" help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0="" alt2"="">1</div><div number2="" index1="" alt1"="">2</div><div number3="" index2="" alt2"="">3</div><div number4="" index3="" alt1"="">4</div></td><td><div><div number1="" index0="" alt2"=""><code functions"="">cd</code> <code string"="">"源码目录/src/tools/gyp"</code></div><div number2="" index1="" alt1"=""> </div><div number3="" index2="" alt2"=""><code comments"=""># 注意Q此处不能用全路径Q不然会出错</code></div><div number4="" index3="" alt1"=""><code plain"="">gyp.bat </code><code string"="">"../../client/windows/breakpad_client.gyp"</code></div></div></td></tr></tbody></table></div></div> <p>此时Q在src/client/windows下就可以看到生成好的breakpad_client.sln了。运行吧Q?/p> <p>5. Hello World!<br /> ~译build allQ现在一般是不会报错了,如果报错Q请查是不是漏了什么步骤,特别是补丁?br /> ~译完成之后Q运行crash_generation_app吧,q是他的试E序Qdump的默认位|保存在C:Dumps下,h意先建立好目录,不然会无法用?br /> 启动试E序之后Q此时还不能抓取dumpQ因个是breakpad中的服务器端Q需要再启动一个测试程序,在第二个试E序中,我们可以试验Client菜单中的各种崩溃了。这些崩溃都会被抓住转存到C:Dumps目录下?/p> <h3><strong>如何使用breakpad</strong></h3> <p> </p> <p>在Windows?a target="_blank">使用breakpad的方?/a>很简单,只需要创Z个ExceptionHandler的类卛_Q大家可以在crash_generation_appq个工程中找到示例代码,也可以直?a target="_blank">UL</a>WikiQ上面说的也很详l?/p> <p>1.q程内抓取Dump文g</p> <p>q程内抓取Dump文g是最单的breakpad的用法。用方法很单:</p> <div><div class="xzlxjrp" id="highlighter_394770" cpp"=""><div><a command_help="" help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0="" alt2"="">1</div><div number2="" index1="" alt1"="">2</div><div number3="" index2="" alt2"="">3</div><div number4="" index3="" alt1"="">4</div><div number5="" index4="" alt2"="">5</div><div number6="" index5="" alt1"="">6</div><div number7="" index6="" alt2"="">7</div><div number8="" index7="" alt1"="">8</div><div number9="" index8="" alt2"="">9</div><div number10="" index9="" alt1"="">10</div><div number11="" index10="" alt2"="">11</div><div number12="" index11="" alt1"="">12</div><div number13="" index12="" alt2"="">13</div><div number14="" index13="" alt1"="">14</div><div number15="" index14="" alt2"="">15</div><div number16="" index15="" alt1"="">16</div><div number17="" index16="" alt2"="">17</div><div number18="" index17="" alt1"="">18</div><div number19="" index18="" alt2"="">19</div><div number20="" index19="" alt1"="">20</div><div number21="" index20="" alt2"="">21</div></td><td><div><div number1="" index0="" alt2"=""><code keyword="" bold"="">const</code> <code plain"="">std::wstring s_strCrashDir = L</code><code string"="">"c:\dumps"</code><code plain"="">;</code></div><div number2="" index1="" alt1"=""> </div><div number3="" index2="" alt2"=""><code color1="" bold"="">bool</code></div><div number4="" index3="" alt1"=""><code plain"="">InitBreakpad()</code></div><div number5="" index4="" alt2"=""><code plain"="">{</code></div><div number6="" index5="" alt1"=""><code spaces"="">    </code><code plain"="">google_breakpad::ExceptionHandler *pCrashHandler =</code></div><div number7="" index6="" alt2"=""><code spaces"="">        </code><code keyword="" bold"="">new</code> <code plain"="">google_breakpad::ExceptionHandler(s_strCrashDir,</code></div><div number8="" index7="" alt1"=""><code spaces"="">        </code><code plain"="">onExceptionFilter,</code></div><div number9="" index8="" alt2"=""><code spaces"="">        </code><code plain"="">onMinidumpDumped,</code></div><div number10="" index9="" alt1"=""><code spaces"="">        </code><code plain"="">NULL,</code></div><div number11="" index10="" alt2"=""><code spaces"="">        </code><code plain"="">google_breakpad::ExceptionHandler::HANDLER_ALL,</code></div><div number12="" index11="" alt1"=""><code spaces"="">        </code><code plain"="">MiniDumpNormal,</code></div><div number13="" index12="" alt2"=""><code spaces"="">        </code><code plain"="">NULL,</code></div><div number14="" index13="" alt1"=""><code spaces"="">        </code><code plain"="">NULL);</code></div><div number15="" index14="" alt2"=""> </div><div number16="" index15="" alt1"=""><code spaces"="">    </code><code keyword="" bold"="">if</code><code plain"="">(pCrashHandler == NULL) {</code></div><div number17="" index16="" alt2"=""><code spaces"="">        </code><code keyword="" bold"="">return</code> <code keyword="" bold"="">false</code><code plain"="">;</code></div><div number18="" index17="" alt1"=""><code spaces"="">    </code><code plain"="">}</code></div><div number19="" index18="" alt2"=""> </div><div number20="" index19="" alt1"=""><code spaces"="">    </code><code keyword="" bold"="">return</code> <code keyword="" bold"="">true</code><code plain"="">;</code></div><div number21="" index20="" alt2"=""><code plain"="">}</code></div></div></td></tr></tbody></table></div></div> <p>2.q程外抓取Dump文g</p> <p>使用q程外抓取DumpӞ需要指定服务端和客LQ在服务端中需要创建CrashGenerationServer的实例,而在客户端中则只需要创建ExceptionHandler卛_。此外,如果服务端自己需要抓q程内的DumpQ请pipe的参数置为NULL?/p> <div><div class="jlzvxpn" id="highlighter_709395" cpp"=""><div><a command_help="" help"="">?</a></div><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td><div number1="" index0="" alt2"="">1</div><div number2="" index1="" alt1"="">2</div><div number3="" index2="" alt2"="">3</div><div number4="" index3="" alt1"="">4</div><div number5="" index4="" alt2"="">5</div><div number6="" index5="" alt1"="">6</div><div number7="" index6="" alt2"="">7</div><div number8="" index7="" alt1"="">8</div><div number9="" index8="" alt2"="">9</div><div number10="" index9="" alt1"="">10</div><div number11="" index10="" alt2"="">11</div><div number12="" index11="" alt1"="">12</div><div number13="" index12="" alt2"="">13</div><div number14="" index13="" alt1"="">14</div><div number15="" index14="" alt2"="">15</div><div number16="" index15="" alt1"="">16</div><div number17="" index16="" alt2"="">17</div><div number18="" index17="" alt1"="">18</div><div number19="" index18="" alt2"="">19</div><div number20="" index19="" alt1"="">20</div><div number21="" index20="" alt2"="">21</div><div number22="" index21="" alt1"="">22</div><div number23="" index22="" alt2"="">23</div><div number24="" index23="" alt1"="">24</div><div number25="" index24="" alt2"="">25</div><div number26="" index25="" alt1"="">26</div><div number27="" index26="" alt2"="">27</div><div number28="" index27="" alt1"="">28</div><div number29="" index28="" alt2"="">29</div><div number30="" index29="" alt1"="">30</div><div number31="" index30="" alt2"="">31</div><div number32="" index31="" alt1"="">32</div><div number33="" index32="" alt2"="">33</div><div number34="" index33="" alt1"="">34</div><div number35="" index34="" alt2"="">35</div><div number36="" index35="" alt1"="">36</div><div number37="" index36="" alt2"="">37</div><div number38="" index37="" alt1"="">38</div><div number39="" index38="" alt2"="">39</div><div number40="" index39="" alt1"="">40</div><div number41="" index40="" alt2"="">41</div><div number42="" index41="" alt1"="">42</div><div number43="" index42="" alt2"="">43</div><div number44="" index43="" alt1"="">44</div></td><td><div><div number1="" index0="" alt2"=""><code keyword="" bold"="">const</code> <code color1="" bold"="">wchar_t</code> <code plain"="">s_pPipeName[] = L</code><code string"="">"\\.\pipe\breakpad\crash_handler_server"</code><code plain"="">;</code></div><div number2="" index1="" alt1"=""><code keyword="" bold"="">const</code> <code plain"="">std::wstring s_strCrashDir = L</code><code string"="">"c:\dumps"</code><code plain"="">;</code></div><div number3="" index2="" alt2"=""> </div><div number4="" index3="" alt1"=""><code color1="" bold"="">bool</code></div><div number5="" index4="" alt2"=""><code plain"="">InitBreakpad()</code></div><div number6="" index5="" alt1"=""><code plain"="">{</code></div><div number7="" index6="" alt2"=""><code spaces"="">    </code><code plain"="">google_breakpad::CrashGenerationServer *pCrashServer =</code></div><div number8="" index7="" alt1"=""><code spaces"="">        </code><code keyword="" bold"="">new</code> <code plain"="">google_breakpad::CrashGenerationServer(s_pPipeName,</code></div><div number9="" index8="" alt2"=""><code spaces"="">        </code><code plain"="">NULL,</code></div><div number10="" index9="" alt1"=""><code spaces"="">        </code><code plain"="">onClientConnected,</code></div><div number11="" index10="" alt2"=""><code spaces"="">        </code><code plain"="">NULL,</code></div><div number12="" index11="" alt1"=""><code spaces"="">        </code><code plain"="">onClientDumpRequest,</code></div><div number13="" index12="" alt2"=""><code spaces"="">        </code><code plain"="">NULL,</code></div><div number14="" index13="" alt1"=""><code spaces"="">        </code><code plain"="">onClientExited,</code></div><div number15="" index14="" alt2"=""><code spaces"="">        </code><code plain"="">NULL,</code></div><div number16="" index15="" alt1"=""><code spaces"="">        </code><code keyword="" bold"="">true</code><code plain"="">,</code></div><div number17="" index16="" alt2"=""><code spaces"="">        </code><code plain"="">&s_strCrashDir);</code></div><div number18="" index17="" alt1"=""> </div><div number19="" index18="" alt2"=""><code spaces"="">    </code><code keyword="" bold"="">if</code><code plain"="">(pCrashServer == NULL) {</code></div><div number20="" index19="" alt1"=""><code spaces"="">        </code><code keyword="" bold"="">return</code> <code keyword="" bold"="">false</code><code plain"="">;</code></div><div number21="" index20="" alt2"=""><code spaces"="">    </code><code plain"="">}</code></div><div number22="" index21="" alt1"=""> </div><div number23="" index22="" alt2"=""><code spaces"="">    </code><code comments"="">// 如果已经服务端已l启动了Q此处启动会p|</code></div><div number24="" index23="" alt1"=""><code spaces"="">    </code><code keyword="" bold"="">if</code><code plain"="">(!pCrashServer->Start()) {</code></div><div number25="" index24="" alt2"=""><code spaces"="">        </code><code keyword="" bold"="">delete</code> <code plain"="">pCrashServer;</code></div><div number26="" index25="" alt1"=""><code spaces"="">        </code><code plain"="">pCrashServer = NULL;</code></div><div number27="" index26="" alt2"=""><code spaces"="">    </code><code plain"="">}</code></div><div number28="" index27="" alt1"=""> </div><div number29="" index28="" alt2"=""><code spaces"="">    </code><code plain"="">google_breakpad::ExceptionHandler *pCrashHandler =</code></div><div number30="" index29="" alt1"=""><code spaces"="">        </code><code keyword="" bold"="">new</code> <code plain"="">google_breakpad::ExceptionHandler(s_strCrashDir,</code></div><div number31="" index30="" alt2"=""><code spaces"="">        </code><code plain"="">onExceptionFilter,</code></div><div number32="" index31="" alt1"=""><code spaces"="">        </code><code plain"="">onMinidumpDumped,</code></div><div number33="" index32="" alt2"=""><code spaces"="">        </code><code plain"="">NULL,</code></div><div number34="" index33="" alt1"=""><code spaces"="">        </code><code plain"="">google_breakpad::ExceptionHandler::HANDLER_ALL,</code></div><div number35="" index34="" alt2"=""><code spaces"="">        </code><code plain"="">MiniDumpNormal,</code></div><div number36="" index35="" alt1"=""><code spaces"="">        </code><code plain"="">(pCrashServer == NULL) ? s_pPipeName : NULL, </code><code comments"="">// 如果是服务端Q则直接使用q程内dump</code></div><div number37="" index36="" alt2"=""><code spaces"="">        </code><code plain"="">NULL);</code></div><div number38="" index37="" alt1"=""> </div><div number39="" index38="" alt2"=""><code spaces"="">    </code><code keyword="" bold"="">if</code><code plain"="">(pCrashHandler == NULL) {</code></div><div number40="" index39="" alt1"=""><code spaces"="">        </code><code keyword="" bold"="">return</code> <code keyword="" bold"="">false</code><code plain"="">;</code></div><div number41="" index40="" alt2"=""><code spaces"="">    </code><code plain"="">}</code></div><div number42="" index41="" alt1"=""> </div><div number43="" index42="" alt2"=""><code spaces"="">    </code><code keyword="" bold"="">return</code> <code keyword="" bold"="">true</code><code plain"="">;</code></div><div number44="" index43="" alt1"=""><code plain"="">}</code></div></div></td></tr></tbody></table></div></div> <p>使用breakpad的时候,有两个地斚w要注意:<br /> 1. 记得把breakpad的solution下的几个工程Q包含到你开发的工程中,或者直接包含他们的lib?br /> commonQ基功能Q包含一个对GUID的封装和http上传的类?br /> exception_handlerQ用来捕获崩溃的cR?br /> crash_generation_serverQbreakpad的服务端Q用来在产生崩溃时抓取dump?br /> crash_generation_clientQbreakpad的客LQ用来捕获当前进E的崩溃?/p> <p>2. 在初始化breakpad之前Q记得先创徏好dump文g的目录,不然breakpad服务端将不能正常的写dumpQ这会导致breakpad客户端在崩溃时无限等待服务端dump写完的消息,最后失d应?/p></div><img src ="http://m.shnenglu.com/lauer3912/aggbug/159784.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/lauer3912/" target="_blank">RTY</a> 2011-11-07 21:34 <a href="http://m.shnenglu.com/lauer3912/archive/2011/11/07/159784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>调试Release发布版程序的Crash错误 Q{Q?/title><link>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159781.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 07 Nov 2011 13:15:00 GMT</pubDate><guid>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159781.html</guid><wfw:comment>http://m.shnenglu.com/lauer3912/comments/159781.html</wfw:comment><comments>http://m.shnenglu.com/lauer3912/archive/2011/11/07/159781.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/lauer3912/comments/commentRss/159781.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/lauer3912/services/trackbacks/159781.html</trackback:ping><description><![CDATA[<div><h1><a id="viewpost1_TitleUrl" href="../../Walker/articles/146153.html">调试Release发布版程序的Crash错误 Q{Q?/a></h1> <div> <h2><a id="viewpost1_TitleUrl" href="../../woaidongmao/archive/2011/05/10/146092.html">调试Release发布版程序的Crash错误</a> </h2> <div> <p><a title="http://blog.sina.com.cn/s/blog_48f93b530100fsln.html" >http://blog.sina.com.cn/s/blog_48f93b530100fsln.html</a></p> <p> </p> <p>?span>Windowsq_下用C++开发应用程序,最不想见到的情冉|怕就是程序崩溃,而要惌军_起问题的bugQ最困难的应该就是调试release版本了。因为release版本来就了很多调试信息Q更何况一般都是发布出ȝ用户使用Qcrash的现场很难保留和重现。本文将l出几个解决ҎQ完成对release版应用程序crash错误的调试。(本文只讨论Windowsq_MSVC环境下的调试Q对于其他^台和开发环境没有关注,请大家自己借鉴和尝试。)</span></p> <p> <wbr></p> <p> <wbr> <wbr>  <wbr><strong>Ҏ一Q崩溃地址</strong> <span><strong><span>+ MAP</span></strong></span><strong>文g</strong></p> <p><span> <wbr> <wbr> <wbr> </span>q种Ҏ只能?span><span>VC7</span>以前的版本开发的E序使用?span> <wbr></span></span></p> <p><span> <wbr> <wbr> <wbr> <strong>1</strong></span><strong>、崩溃地址</strong></p> <p><span> <wbr> <wbr> <wbr> <wbr> <wbr>所谓崩溃地址是引vE序崩溃的内存地址Q在<span>WinXP</span>下应用程?span>crash</span>的对话框如下图:</span></p> <p align="center"><a target="_blank"><span><span><img title="clip_image001" alt="clip_image001" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image001_0ad42579-f1c9-44ae-b2f8-b50b8a737e0d.jpg" height="181" width="424" border="0" /></span></span></a></p> <p align="center"><a target="_blank"><span><span><img title="clip_image002" alt="clip_image002" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image002_cb3f00fa-aa22-4301-a42c-92c12dff2ea4.jpg" height="120" width="494" border="0" /></span></span></a></p> <p align="center"><a target="_blank"><span><span><img title="clip_image003" alt="clip_image003" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image003_fea81ac9-41ea-42be-a24f-d0fe99dafa80.jpg" height="380" width="494" border="0" /></span></span></a></p> <p><span> <wbr> <wbr> <wbr> </span><span>上面W?span>2</span>张图中画U线的gؓ<span>crash</span>的代码偏Ud址Q第<span>3</span>张图为即<span>crash</span>l对地址Q一般引?span>crash</span>的原因多为内存操作错误,我们用这两个地址?span>MAP</span>文gp定位出错的代码行?/span></p> <p><span> <wbr> <wbr> <wbr> <strong>2</strong></span><strong>?span><span>MAP</span>文g</span></strong></p> <p><span> <wbr> <wbr> <wbr> MAP</span>文g是记录应用程序信息的文gQ文本文ӞQ里面大概包含了E序的全局W号、源码模块名、源码文件和行号{信息,而这些信息能够帮助我们定位出错的代码行?/p> <p><span> <wbr> <wbr> <wbr> </span>怎样生成<span><span>MAP</span>文g呢??span>VC6</span>ZQ在 <span>Project Settings -> C/C++ <wbr>-> Debug info</span>中,选择 <span>Line Numbers Only </span>Q在 <span>Project Settings -> Link </span>中,选择 <span>Generate mapfile</span>,q在<span>Project Options </span>里面输入 </span><span>/MAPINFO:LINES</span> ?<span><span>/MAPINFO:EXPORTS</span>Q重新编译程序就会生?span>.map</span>文g?/span></p> <p><span> <wbr> <wbr> <wbr> </span>以上讄对应的编译链接选项分别分:</p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>/Zi</span></strong> — 表示生成<span><span>pdb</span>调试信息Q?/span></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>/MAP[:filename]</span></strong> — 表示生成<span><span>map</span>文g名;</span></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>/MAPINFO:EXPORTS <wbr></span></strong>— 表示生成?span><span>map</span>文g中加?span>exported functions</span>Q生?span>DLL</span>文gӞQ?/span></p> <p><span> <wbr> <wbr> <wbr> </span><strong><span>/MAPINFO:LINES <wbr></span></strong>— 表示生成?span><span>map</span>文g中加入代码行信息?/span></p> <p><span> <wbr> <wbr> <wbr> </span>׃<span><span>/MAPINFO:LINES</span>选项?span>VC8</span>以后的版本中不再支持Q因此通过<span>MAP</span>文g中的信息?span>crash</span>地址定位出错代码行就比较困难了,所以这U方案只能在<span>VC7</span>及以前的版本中用?/span></p> <p><span> <wbr> <wbr> <wbr> <wbr>一?span>MAP</span>文g片段CZ如下Q?span> <wbr></span></span></p> <p><span> <wbr> <wbr> <wbr> <a target="_blank"><span><span><img title="clip_image004" alt="clip_image004" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image004_fbf07de5-1b63-4473-917f-f6695904c9be.jpg" height="229" width="494" border="0" /></span> <wbr></span></a> <wbr></span></p> <p><span> <wbr> <wbr> <wbr> <a target="_blank"><span><span><img title="clip_image005" alt="clip_image005" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image005_93f97fcb-0900-4f57-baf2-29a04f821fa2.jpg" height="227" width="494" border="0" /></span></span></a></span></p> <p><span> <wbr> <wbr> <wbr> </span><span>图中<span>Rva+Base</span>列的地址行函数对应的函数l对地址Q?span>Address</span>列中冒号后面的地址为函数相对偏Ud址?span> <wbr> <wbr> <wbr></span></span></p> <p><span> <wbr> <wbr> <wbr> <strong>3</strong></span><strong>、定?span><span>crash</span>代码</span></strong></p> <p><span> <wbr> <wbr> <wbr> </span>有了上面的介l,定位<span><span>crash</span>代码很单了。用下面的公式来q行定位Q?/span></p> <p><span> <wbr> <wbr> <wbr> </span><strong>崩溃行偏U?<span><span>= </span>崩溃地址 <span>- </span>崩溃函数l对地址 <span>+ </span>函数相对偏移</span></strong></p> <p><span> <wbr> <wbr> <wbr> </span>我们首先Ҏ崩溃地址Q绝对地址Q,按照扑ֈW?span><span>2</span>张图?span>Rva+Base</span>列的地址扑ֈ发生崩溃的函敎ͼ卛_溃地址大于该函数行?span>Rva+Base</span>地址且小于下个函数的地址Q,然后扑ֈ该行对应的函数相对偏Ud址Q带入公式中Q就得到了崩溃行偏移Q该DC崩溃行的代码相对于代码所在函数的偏移量。用该值去与第<span>3</span>张图中对应函数冒号后面的偏移量去比较Q最接近的值前面的那个十进制数即ؓ代码所在函C的行受?/span></p> <p><span> <wbr> <wbr> <wbr> ok</span>Q到此我们已l成功找C崩溃的代码行Q只不过q种Ҏq是比较费力Qƈ且限制比较多Q我们看看下面的Ҏ?/p> <p>上篇l出的方案一q要补充几句。通过<span>“crash地址 + MAP文g”来定位出错代码位|虽焉要经q比较复杂的地址计算Q但却是最单实现的方式。如果仅仅想通过崩溃地址定位出错的函敎ͼ更加方便了。我在网上找C个解析MAP文g的小工具Q可以非常清晰的列出每个函数的地址Qƈ且可以将分析表格导出为Excel文g。工具下载地址Q?a ><span>http://e.ys168.com/?tinyfun</span></a></span>Q工L录下VCMapper.exe?/p> <p> <wbr> <wbr> <wbr> 另外上篇主要参考两文章:</p> <p> <wbr> <wbr> <wbr> <a ><span>http://www.vckbase.com/document/viewdoc/?id=908</span></a></p> <p> <wbr> <wbr> <wbr> <a ><span>http://www.vckbase.com/document/viewdoc/?id=1473</span></a></p> <p> <wbr></p> <p> <wbr> <wbr> <wbr> <strong>Ҏ二:崩溃地址<span> + MAP文g + COD文g</span></strong></p> <p> <wbr> <wbr> <wbr> ׃<span>VC8以后的版本都不再支持MAP文g中生代码行信息Q因此我们寻扑֏一U定位方式:COD文g?/span></p> <p> <wbr> <wbr> <wbr> <strong><span>1</span></strong><strong>?span>COD文g</span></strong></p> <p> <wbr> <wbr> <wbr> COD文g是一个包含了汇编码、二q制机器码和源代码对应信息的文gQ每一?span>cpp都对应一个COD文g。通过q个文gQ我们可以非常方便地q行定位?/span></p> <p> <wbr> <wbr> <wbr> ?span>VC6中生成COD文g的设|方式ؓQProject Settings -> C/C++Q在 Category 中?Listing FilesQ在 Listing file type l合框中?AssemblyQMachine codeQand source。在VC8中生成COD文g的设|方式ؓQProject Properties -> C/C++ -> Output Files -> Assembler Output ,选择 AssemblyQMachine codeQand Source(/Facs)?/span></p> <p> <wbr> <wbr> <wbr></p> <p> <wbr> <wbr> <wbr> <strong><span>2</span></strong><strong>、定位崩溃行</strong></p> <p> <wbr> <wbr> <wbr> 下面通过举例q行说明。现在我有一个基于对话框?span>MFC应用E序CrashTestQ在CCrashTestDlg::OnInitDialog函数中写入导致crash的代码语句(W?9行)Q源文g如下Q?/span></p> <p> <wbr> <wbr> <wbr> <a target="_blank"><span><span><img title="clip_image006" alt="clip_image006" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image006_887560ee-8b72-42ea-a623-220dcb47ac1f.jpg" height="169" width="360" border="0" /></span></span></a></p> <p> <wbr> <wbr> <wbr> Ҏ崩溃地址Q?span>0x004012A3Q以及MAP文gQ定位片D图片如下)Q定位crash函数为OnInitDialogQƈ且我们可以很Ҏ地计出崩溃地址相对于崩溃函数的偏移量ؓ 0x004012A3 - 0x004011E0 = 0xC3?/span></p> <p> <wbr> <wbr> <wbr> <a target="_blank"><span><span><img title="clip_image007" alt="clip_image007" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image007_dc9962ce-f969-4c7f-aa0b-e973cac321ce.jpg" height="36" width="494" border="0" /></span></span></a></p> <p> <wbr> <wbr> <wbr> 再来看看<span>CrashTestDlg.cod文gQ我们根据文件中源码信息扑ֈOnInitDialog函数信息片段Q?/span></p> <p> <wbr> <wbr> <wbr> <a target="_blank"><span><span><img title="clip_image008" alt="clip_image008" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image008_00ac91bc-99ca-4d4f-86cd-b0d1d23bb10a.jpg" height="231" width="494" border="0" /></span></span></a></p> <p> <wbr> <wbr> <wbr> 可以看到囄中第一行ؓ<span>OnInitDialog函数汇编代码的v始行Q找?#8220;int * p = NULL;”q一句源码,其前面的98表示q行代码在源文g中的行号Q下面的000c1表示相对于函数开始位|的偏移量,后面?#8220;33 c0”为机器码Q?#8220;xor eaxQeax”为汇~码。那么我们根据前面算出来的偏U量0xC3Q找到对应出错的语句?9行:“*p = 5;”?/span></p> <p> <wbr> <wbr> <wbr> ȝ一下定位步骤:</p> <p> <wbr> <wbr> <wbr> 1) Ҏ公式 <strong><span>崩溃语句在函C偏移地址<span> = 崩溃地址 - 崩溃函数地址</span></span></strong> 计算出偏U量XQ?/p> <p> <wbr> <wbr> <wbr> 2) Ҏ公式 <strong><span>崩溃语句?span>COD文g中地址 = 崩溃函数在COD文g中地址 + <wbr>X</span></span></strong> 计算出地址Y。其中崩溃函数在COD文g中地址为COD文g中函数v始括?#8220;{”后面表明的地址Q一般情况下?x0000Q?/p> <p> <wbr> <wbr> <wbr> 3) Ҏ<span>Y在COD文g中找到对应代码行?/span></p> <p> <wbr> <wbr> <wbr></p> <p> <wbr> <wbr> <wbr> okQ方案二介绍完了。这U方法最大的好处是没?span>VC开发环境版本限Ӟ而且COD文g里面包含的信息更加丰富,不但可以帮助我们定位crashQ还能帮我们分析很多东西。当Ӟq也D~译生成了很多信息文件?/span></p> <p>Ҏ前面两篇博文Q我们要定位崩溃行代码,必须要自己根据相关信息文件进行计。如果需要处理的量比较大Q恐怕会很费力气。有没有更简单快速的办法呢?</p> <p> <wbr> <wbr> <wbr> 最直接的想法就是写一个小工具Q根据规则和信息q行自动定位Q不q开发v来也是要费一番功夫的。o人开心的是,我们可以扑ֈcM的工P而且是开源免费的Q程序员的世界也许很多时候都是这么单U而乐于分享!</p> <p> <wbr> <wbr> <wbr></p> <p> <wbr> <wbr> <wbr> <strong>Ҏ三:崩溃地址<span> + PDB文g + CrashFinder</span></strong></p> <p> <wbr><wbr> <wbr><wbr> <wbr><wbr> CrashFinder是一个开源工P作者是<span>John RobbinQ大家可以去他的blog上去扑օ于CrashFinder的信息。我们这里以CrashFinder2.5版本Z介绍Q相x章链接ؓQ?a ><span>http://www.wintellect.com/CS/blogs/jrobbins/archive/2006/04/19/crashfinder-returns.aspx</span></a></span></p> <p> <wbr><wbr> <wbr><wbr> <wbr> <strong><span>1</span></strong><strong>?span>PDB文g</span></strong></p> <p> <wbr> <wbr> <wbr> PDBQ?span>Program DatabaseQ文件中包含了exeE序所有的调试相关信息Q具体可以查阅MSDN。当~译选项讄?ZiQ链接选项讄?DEBUGQ?OPT:REFӞ׃生成工程?pdb文g。具体到VC2005中,是 Project Propertise -> C/C++ -> General -> Debug Information Format 设|ؓ Program DatabaseQ?ZiQ,Linker -> Debugging -> Generate Debug Info 设|ؓ YesQ?DebugQ,Linker -> Optimization -> References <wbr>设|ؓ Eliminate <wbr>Unreferenced DataQ?OPT:REFQ?/span></p> <p> <wbr> <wbr> <wbr> 只要讄以上选项Q?span>release版本也能生成PDB文g。当Ӟ对应的应用程序也会稍大?/span></p> <p> <wbr> <wbr> <wbr> <strong><span>2</span></strong><strong>?span>CrashFinder</span></strong></p> <p> <wbr> <wbr> <wbr> CrashFinder能够q行需要两个条Ӟ一是系l必要?span>dbghelp.dll文gQ二是PDB文g必须与exe文g在一个\径下。对于dbghelp.dllQ一般在pȝsystem32路径下都有,如果没有下蝲一个放到这个目录下可以了?/span></p> <p> <wbr> <wbr> <wbr> 先看一?span>CrashFinder的界面?/span></p> <p> <wbr> <wbr> <wbr></p> <p align="center"><a target="_blank"><span><span><img title="clip_image009" alt="clip_image009" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image009_60e666ee-6f31-4132-929c-3ef20c5a9fde.jpg" height="327" width="494" border="0" /></span></span></a></p> <p> <wbr> <wbr> <wbr> 用v来也非常单。首先选择<span>File->New或点dh新徏按钮Q选择要调试的exe文g打开Q会发现exe及所依赖的dll文g信息都已l加载进来。在下半部分的编辑框中输入崩溃地址Q?6q制Q,点右边的“Find”按钮Q就会在下面昄崩溃的源文g路径、名UC及崩溃所在行号了Q如下图所C?/span></p> <p align="center"><a target="_blank"><span><span><img title="clip_image010" alt="clip_image010" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image010_4b25ce3c-1bea-4818-be58-230d6cba8a6a.jpg" height="370" width="494" border="0" /></span></span></a></p> <p> <wbr> <wbr> <wbr> ?span>CrashFinderq行crash定位真的非常方便。但是我在用过E中发现了一个bugQ每ơ启动程序后Q直接新建的话加载进来的exe模块都显C叉Q提C找不到debug symbols。但是用打开按钮随便打开一个文件失败后Q再新徏p成功。猜可能是直接新徏Q定位PDB文g时的路径不对引v的。有源码Q但是懒的看了呵呵,大家感兴可以试一下?/span></p> <p> <wbr> <wbr> <wbr> 好了Q方案三׃l到q里Q后面还有更加强大的Ҏ<span> : )</span></p> <p>前面几个Ҏ都是直接定位<span>crash的代码位|,但是在比较大型的E序中,只知道这个信息还是远q不够的Q我们希望知道更多关于调用函数顺序及变量值等信息Q也是crash时调用堆栈信息?/span></p> <p> <wbr></p> <p> <wbr> <wbr> <wbr> <strong>Ҏ四:<span>SetUnhandledExceptionFil<wbr>ter + StackWalker</span></strong></p> <p> <wbr> <wbr> <wbr> q个Ҏ需要自己动手往工程里添加代码了。要实现上面的想法,需要做两g事情Q?span>1、需要在crash时有Z对程序堆栈进行处理;2、对堆栈信息q行攉?/span></p> <p> <wbr> <wbr> <wbr> <strong><span>1</span></strong><strong>?span>SetUnhandleExceptionFilt<wbr>er函数</span></strong></p> <p> <wbr> <wbr> <wbr> Windowsq_下的<span>C++E序异常通常可分ZU:l构化异常(Structured ExceptionQ可以理解ؓ与操作系l相关的异常Q和C++异常。对于结构化异常处理QSEHQ,可以扑ֈ很多资料Q在此不l说。对于crash错误Q一般由未被正常捕获的异常引PWindows操作pȝ提供了一个API函数可以在程序crash之前有机会处理这些异常,是SetUnhandleExceptionFilt<wbr>er函数。(C++也有一个类似函数set_terminate可以处理未被捕获的C++异常。)</span></p> <p> <wbr> <wbr> <wbr> SetUnhandleExceptionFilt<wbr>er函数声明如下Q?/p> <p> <wbr> <wbr> <wbr> LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFil<wbr>ter(<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> LPTOP_LEVEL_EXCEPTION_FILTER <em>lpTopLevelExceptionFilte<wbr>r</em><br /> <wbr> <wbr> <wbr> );</p> <p> <wbr> <wbr> <wbr> 其中<span> LPTOP_LEVEL_EXCEPTION_FILTER 定义如下Q?/span></p> <p> <wbr> <wbr> <wbr> typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(<br /> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> __in struct _EXCEPTION_POINTERS *ExceptionInfo<br /> <wbr> <wbr> <wbr> );<br /> <wbr> <wbr> <wbr> typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;</p> <p> <wbr> <wbr> <wbr> 单来_<span>SetUnhandleExceptionFilt<wbr>er允许我们讄一个自q函数作ؓ全局SEHqo函数Q当E序crash前会调用我们的函数进行处理。我们可以利用的?_EXCEPTION_POINTERS l构cd的变量ExceptionInfoQ它包含了对异常的描qC及发生异常的U程状态,qo函数可以通过q回不同的值来让系ll运行或退出应用程序?/span></p> <p> <wbr> <wbr> <wbr> 关于<span> SetUnhandleExceptionFilt<wbr>er 函数的具体用法和CZ请参考MSDN?/span></p> <p> <wbr></p> <p> <wbr> <wbr> <wbr> <strong><span>2</span></strong><strong>?span>StackWalker</span></strong><br /> <wbr> <wbr> <wbr> 现在我们已经有机会可以在<span>crash之前对程序状态信息进行处理了Q只需要生成ƈ保存堆栈信息大功告成了。Windows的dbghelp.dll库提供了一个函数可以得到当前堆栈信息:StackWalk64Q在Win2K以前版本中ؓStackWalkQ。该函数声明如下Q?/span></p> <p> <wbr> <wbr> <wbr> BOOL WINAPI StackWalk64(<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> DWORD <em>MachineType</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hProcess</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hThread</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in_out <wbr> <wbr> <wbr> <wbr> <wbr> LPSTACKFRAME64 <em>StackFrame</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in_out <wbr> <wbr> <wbr> <wbr> <wbr> PVOID <em>ContextRecord</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PREAD_PROCESS_MEMORY_ROUTINE64 <em>ReadMemoryRoutine</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PFUNCTION_TABLE_ACCESS_ROUTINE64 <em>FunctionTableAccessRouti<wbr>ne</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PGET_MODULE_BASE_ROUTINE64 <em>GetModuleBaseRoutine</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PTRANSLATE_ADDRESS_ROUTINE64 <em>TranslateAddress</em><br /> <wbr> <wbr> <wbr> );<br /> <wbr> <wbr> <wbr> 该函数的具体用法可以参?span>MSDN。在q里推荐一个牛人写好的StackWalkerQ可以直接拿来用Q开源的。StackWalker提供了一个基c,l出了几个简单的接口Q可以方便地生成堆栈信息Qƈ且支持一pdVC版本Q非常好用。我们可以自己写一个子c,q载虚函数OnOutputQ就可以堆栈信息输Zؓ特定格式了。StackWalker的地址为:<a ><span>http://www.codeproject.com/KB/threads/StackWalker.aspx</span></a></span>?/p> <p> <wbr> <wbr> <wbr> 不过对于<span>Release版本来说QStackWalk64函数获得的堆栈信息有可能不完整。如果异常是由MFC的模块抛出,那么获得的堆栈可能缺前面调用模块信息。另外,StackWalk64需要最新的dbghelp.dll文g支持才能工作Q要正确输出crash的函数名和行P需要要pdb文g支持。以上不x可能影响输出信息的完整性和效果Q而对于发布在外的E序Q要带上pdb文g几乎不可能,因此q个Ҏq是有缺憄Q比较适用于本地的release版本调试?/span></p> <p> <wbr> <wbr> <wbr> 下一我们将介绍一个更加完善的解决Ҏ</p> <p>当我们把自己?span>release版本E序发布出去以后Q一般都是在用户的机器上q行。这U情况下Q对于第四种ҎQ因为需要pdb文g才能够正生成堆栈调用的函数行号及代码行P因此Ҏ四只适用于本地release版的调试Q否则只能生成不完整的堆栈信息。对于前三种ҎQ其实只需要用户告知崩溃地址Q然后在本地查找crash地址可以了Q但是定位crash的过E非怸方便Q如果crash的情冉|较多Q前三种Ҏ都不合适。而且Q前三种Ҏ均不能生成堆栈调用信息,对于debug的作用有限?/span></p> <p> <wbr> <wbr> <wbr> 下面我们来看一个更加完善的解决Ҏ?/p> <p> <wbr></p> <p> <wbr> <wbr> <wbr> <strong>Ҏ五:<span>SetUnhandledExceptionFil<wbr><wbr>ter + Minidump</span></strong></p> <p> <wbr> <wbr> <wbr> SetUnhandleExceptionFilt<wbr>er函数我们已经介绍q了Q本Ҏ的思\q是要利用我们自q异常处理函数Q来生成<span>minidump文g?/span></p> <p> <wbr> <wbr> <wbr> <strong><span>1</span></strong><strong>?span>Minidump概念</span></strong></p> <p> <wbr> <wbr> <wbr> minidumpQ小存储器{储)可以理解Z?span>dump文gQ里面记录了能够帮助调试crash的最有用信息。实际上Q如果你?pȝ属?-> 高 -> 启动和故障恢?-> 讄 -> 写入调试信息 中选择“内存{?64 KB)”的话Q当pȝ意外停止旉会在C:\Windows\Minidump\路径下生成一?dmp后缀的文Ӟq个文g是minidump文gQ只不过q个是内核态的minidump?/span></p> <p> <wbr> <wbr> <wbr>我们要生成的是用h的<span>minidumpQ文件中包含了程序运行的模块信息、线E信息、堆栈调用信息等。而且ZW合其mini的特性,dump文g是压~过的?/span></p> <p> <wbr> <wbr> <wbr> <strong><span>2</span></strong><strong>、生?span>minidump文g</span></strong></p> <p> <wbr> <wbr> <wbr> 生成<span>minidump文g的API函数是MiniDumpWriteDumpQ该函数需要dbghelp.lib支持Q其原型如下:</span></p> <p> <wbr> <wbr> <wbr> BOOL WINAPI MiniDumpWriteDump(<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hProcess</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> DWORD <em>ProcessId</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> HANDLE <em>hFile</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> MINIDUMP_TYPE <em>DumpType</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PMINIDUMP_EXCEPTION_INFORMATION <em>ExceptionParam</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PMINIDUMP_USER_STREAM_INFORMATION <em>UserStreamParam</em>,<br /> <wbr> <wbr> <wbr> <wbr> <wbr> __in <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> PMINIDUMP_CALLBACK_INFORMATION <em>CallbackParam</em><br /> <wbr> <wbr> <wbr> );</p> <p> <wbr> <wbr> <wbr> 在我们的异常处理函数中加入以下代码:</p> <p> <wbr> <wbr> <wbr> HANDLE hFile = ::CreateFile( _T("E:\\dumpfile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);<br /> <wbr> <wbr> <wbr> <wbr> if( hFile != INVALID_HANDLE_VALUE)<br /> <wbr> <wbr> <wbr> <wbr> {<br /> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> MINIDUMP_EXCEPTION_INFORMATION einfo;<br /> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> einfo.ThreadId = ::GetCurrentThreadId();<br /> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> einfo.ExceptionPointers = pExInfo;<br /> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> einfo.ClientPointers = FALSE;</p> <p> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &einfo, NULL, NULL);<br /> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> ::CloseHandle(hFile);<br /> <wbr> <wbr> <wbr> <wbr> }</p> <p> <wbr> <wbr> <wbr> 其中Q?span>pExInfo变量为异常处理函数PEXCEPTION_POINTERScd的参数。具体请参考MSDN?/span></p> <p> <wbr> <wbr> <wbr> <strong><span>3</span></strong><strong>、调?span>minidump</span></strong></p> <p> <wbr> <wbr> <wbr> <wbr>调试<span>dump文g首先需要pdb文gQ因此我们buildE序旉要设|?Debug Infomation Format ?“Program DatabaseQ?ZiQ?#8221;。其ơ,我们q要保所用的dump文g与源代码、exe、pdb文g版本是一致的Q这要求我们必须l护好程序版本信息?/span></p> <p> <wbr> <wbr> <wbr> 调试<span>minidump最方便的环境就是VS了,我们只要?dmp?exe?pdb文g攑֜一个\径下Q保证源代码文g的\径与~译时的路径一致就可以了,剩下的就是VS帮我们完成。双?dmp文g或者在文g打开工程中选择“dump files”Q加载dump文gQ然后按F5q行p直接恢复crash时的现场了,你可以定位crash的代码,可以查看调用堆栈Q可以查看线E和模块信息...一切都跟你讄断点调试一P太强大了Q看个截囑֐?/span></p> <p><a target="_blank"><span><span><img title="clip_image012" alt="clip_image012" src="../../images/cppblog_com/woaidongmao/WindowsLiveWriter/ReleaseCrash_A881/clip_image012_3df8e4ef-d0e8-49d3-a9af-47f58cc713c6.jpg" height="469" width="616" border="0" /></span></span></a></p> <p> <wbr> <wbr> <wbr> 需要注意的是,对于<span>release版的E序来说Q很多代码是l过~译器优化过的,因此定位的时候可能会有所偏差Q大家可以考虑讄选项L代码优化?/span></p> <p> <wbr> <wbr> <wbr> 其他可以调试<span>minidump的工兯有WinDbg{,大家可以查阅相关资料?/span></p> <p> <wbr> <wbr> <wbr> 本文主要参考了q篇文章Q?span><a ><span>http://vicchina.51.net/research/other/seh/minidumps/intro.htm</span></a></span>?/p> <p> <wbr> <wbr> <wbr> 下一,我们给Z个调?span>release发布E序的完解x案,适合用户量较大的应用发布E序的调试?/span></p> <p>上一我们已l给ZҎQ能够非常方便的通过<span>dump文g对crash错误q行调试和定位;从整个流E上看还差最后一步,x样拿到crash时生的dump文g。如果可以让用户把文件发送过来自然不错,但对于类似免费共享Y件等在互联网上发布的E序呢?我们的用h不确定的Q而且用户量有可能非常大,即我们能想办法联系到用PM能挨个去攉crash信息吧?/span></p> <p> <wbr> <wbr> <wbr> 我们需要一U方案,能够提供<span>crash信息汇报功能?/span></p> <p> <wbr> <wbr> <wbr> 我们可以架设一台服务器专门q行信息攉Q只要客L?span>crash时正汇报即可,但是相应的维护成本和开发难度也不可忽视。有没有更简单的Ҏ呢?q记得我的博?#8220;<a target="_blank"><span><span>为程序添加自动发送Email</span><span>功能</span></span></a>”</span>吗?q就是简单有效的ҎQ?/p> <p> <wbr></p> <p> <wbr> <wbr> <wbr> <strong>Ҏ六:<span>minidump + email</span></strong></p> <p> <wbr> <wbr> <wbr> 我们只需要在异常处理Ӟ先生?span>minidump信息文gQ再用email方式文件发送到指定邮箱p了。剩下的是我们每天查看邮箱Q提取dump文gq行调试了?/span></p> <p> <wbr> <wbr> <wbr> <strong><span>1</span></strong><strong>?span>Email功能</span></strong></p> <p> <wbr> <wbr> <wbr> 首先我们来看一?span>email发送都需要哪些相关信息?/span></p> <p> <wbr> <wbr> <wbr> a、发送端邮箱帐户Q?/p> <p> <wbr> <wbr> <wbr> b、接收端邮箱帐户Q?/p> <p> <wbr> <wbr> <wbr> c?span>email标题Q一般应有Y件名U及版本信息Q?/span></p> <p> <wbr> <wbr> <wbr> d?span>email正文Q一般应有简单的crash信息提示Q以区别不同原因造成的crashQ?/span></p> <p> <wbr> <wbr> <wbr> <wbr>e?span>email附gQ当然就是我们的dump文g了,q可以加上Y件生成的log文g{?/span></p> <p> <wbr> <wbr> <wbr> 当然Q对于标题应该尽量多加一些信息区别引?span>crash的原因,比如crash的地址信息加到标题中;因ؓ当每天有成百上千的crash汇报上来Q重复的crash占大多数Q把旉都花在区分它们n上有点太费。由此看来,前面Ҏ中提到的StackWalkerq是有些用处的,我们可以用它来生成一些crash的文字描qC息,写到标题或正文中厅R?/span></p> <p> <wbr> <wbr> <wbr> dump文g的大是否适合作ؓ邮g的附件呢Q实际上<span>minidump产生的文件一般在几K到几十K之间Q作为email的附件没有Q何问题?/span></p> <p> <wbr> <wbr> <wbr> 关于发?span>email相关技术细节,已经?#8220;<a target="_blank"><span><span>为程序添加自动发送Email</span><span>功能</span></span></a>”</span>文中介绍了,大家可以参考。其实,Ҏ受邮׃邮g的处理还是很Ҏ费力的,大家可以考虑写一些脚本将处理程自动化,提高效率?/p> <p> <wbr> <wbr> <wbr> <strong><span>2</span></strong><strong>?span>google breakpad</span></strong></p> <p> <wbr> <wbr> <wbr> google breakpad是一个开源的跨^?span>crash reportpȝQ光从开源和跨^台这两个特点上来看,它就以U的上是一个完善而有效的工具了。其实,breakpad在整个crash report层次上给Z一个系l的解x案,也就是说它几乎能适应各种软g、各U^台的应用要求?/span></p> <p> <wbr> <wbr> <wbr> breakpad的整体思\跟上面介l的Ҏ是相似的Q只不过最后提?span>dump文g的方式更加完善。大家有兴趣可以d的官方网址查阅相关资料Q?a ><span>http://code.google.com/p/google-breakpad/</span></a></span>?/p> <p> <wbr></p> <p> <wbr> <wbr> <wbr> okQ关于调?span>release发布E序的crash错误pd文章写完了。这几篇文章l出的方案由单到复杂Q由陋到完善Q对crash调试有了一个比较全面的ȝ。当Ӟ其中涉及到的概念和技术还很多Q需要我们去不断学习和领悟,也希望大家能够互怺?/span></p> </div> </div></div><img src ="http://m.shnenglu.com/lauer3912/aggbug/159781.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/lauer3912/" target="_blank">RTY</a> 2011-11-07 21:15 <a href="http://m.shnenglu.com/lauer3912/archive/2011/11/07/159781.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DevGuide 如何有助于谷?c + + 试框架 阶段实施特色http://m.shnenglu.com/lauer3912/archive/2011/09/26/156885.htmlRTYRTYMon, 26 Sep 2011 12:55:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156885.htmlhttp://m.shnenglu.com/lauer3912/comments/156885.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156885.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/156885.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/156885.html
搜烦 
 对于  
DevGuide  
如何有助于谷?c + + 试框架 
阶段实施特色
?a target="_top" style="color: #0000cc; white-space: nowrap; ">w...@google.com更新?span title="Thu Aug 19 13:53:46 2010">2010 q?8 ?19 ?/span>

如果您有兴趣了解h试Q从来源Q或zN的想法或修改目?建筑内部本文档将是你?/p>

?/font>

首先Q让我们l您目的一些背景?/p>

发牌

Ҏ新的 BSD 许可?/a>提供所有谷歌测试源和预~译的程序包.

h试C֌

h试C֌已经存在Q主要是透过讨论l?/a>Q?a rel="nofollow" target="_top" style="color: #0000cc; ">问题跟踪?/a>和幅度较,源代码管理储存库?/font>你肯定鼓励参与讨论,您还可以帮助我们保持高集团的效果后,促进在此处列出的准则?/font>

请将友好

昄的礼貌和重他h是在h文化的重要组成部分和我们强烈鼓励大家参与h试开发加盟不接受?/font>当然Q礼貌ƈ不相同,未有性地不同意对方,但它意味着枚D 42 技术原因某徏议可能不是最佳选择时我们应该互相尊重?/font>没有理由拮抗或轻视向M人真诚地试图q行的讨ZA献?/font>

肯定的是Qc + + 试是严肃的商业和其它的东西Q但它也有很多乐?/font>让我们保持这U方式?/font>让我们努力成为所有开放源码中最友好的社Z一?/font>

讨论h试的位|?/font>

一如既往Q讨歌测试的官方h c + + 试框架的讨论组?/font>你不必实际提交代码,以便{?/font>您的参与本n是宝늚贡献?/font>

使用代码

如果你想把你的手弄脏与内h试的代码,q是你的部分?/p>

{և颠覆的来?/font>

{ևh试源是最有用的如果您计划调整自己?/font>h试使用颠覆客户端,?Google Code 上承载的M其他目{և源?/font>请如何做qg事的源代码访问页Q参阅该指o?/font>

从源代码~译

一旦您{և该代码,您可以找到有兛_何编译它?a rel="nofollow" target="_top" style="color: #0000cc; ">自述文g中的说明?/p>

一个测试框架是不好Q如果本w不d的测试?/font>试应编写的M新的代码Qƈ应在提交审查不打破现有的试验证更改?/font>要执行测试,按照自述文g中的说明Qƈ验证不存在Q何故障?/font>

贡献代码

我们很激动谷歌测试现在是开源的和希望从C会得到很大的修补程序?/font>在您最喜爱?IDE 点燃q开始地敲击着q项新功能之前,不过Q请花时间阅L节和理解q一q程?/font>管它看h严格Q我们要保持高标准的质量基本代码?/font>

参与者的许可协议

我们可以接受M代码之前Q您必须注册参与者许可协?(CLA)?/font>p亚a怿护您和我们?/font>

  • 如果您是个h写作原始源代码,你知道你自己的知识权,那么你需要签|?a rel="nofollow" target="_top" style="color: #0000cc; ">个别p亚a?/a>.
  • 如果你工作的公司惌允许您将您的工作有助于谷歌测试,然后需要签|?a rel="nofollow" target="_top" style="color: #0000cc; ">公司p亚a?/a>.

按照上面的两个链接访问相应的p亚a酸和{vq返回它的说明?/p>

~码风格

若要保持源一致的?可读?diffable 和易于合qӞ我们使用相当L的编码风|如由google styleguide目定义?/font>预计所有修补程序将W合所q的风格在这?/a>.

更新生成的代?/font>

h试的源代码文g的一些由泵工?QPython 脚本Q?生成?/font>如果您需要更新这些文Ӟ请修Ҏ Q?tt style="font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; max-width: 66em; ">foo.h.pumpQ,q新生成的 c + + 文g使用c?/font>您可以阅?a target="_top" style="color: #0000cc; ">PumpManual的详l信息?/font>

提交的修补程?/font>

请不要提交代码?/font>下面是您需要做什么:

  1. 通常您应q行更改 SVN 树干的分支或标记Q而不是针对后面的两个版本控制Q应被视Z要ؓ只读?/li>
  2. 军_您想提交哪些的代码?/font>提交应解决一个问题中?a rel="nofollow" target="_top" style="color: #0000cc; ">Google 试问题跟踪?/a>的一l更攏V?/font>请不要؜合多个逻辑更改每提交,因ؓ它历史难追?/font>如果你想有所改变Q在问题跟踪器没有一个相应的问题Q请创徏一个?/font>
  3. 此外Q协调与团队成员的问题上列出?/font>q可以确保工作不正在复制和沟通你的计划早一般也会导致更好的修补E序?/font>
  4. 保您的代码坚守h试源代码样?/a>.
  5. 保您的代码的单元测试?/li>
  6. {֐参与者的许可协议?/li>
  7. 创徏使用svn diff修补E序文g.
  8. 我们使用里特韦尔q行Z web 的代码审查?/font>您可以阅L兌工具在这?/a>?/font>当您准备qA时上, 传您的修补程序通过冉q知googletestframework@googlegroups.com作出讨?/font>有几U方法可以上载该修补E序?/font>我们使用upload_gtest.py脚本中,您可以查?tt style="font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; max-width: 66em; ">脚本 / SVN 树干中的文g夏V?/font>

h试独立自主

h试工程团队的当前成员目前是唯一的独立自丅R?/font>在吃自己的试用的伟大传统Q我们亦会规定每个新来赚取权成ؓ委员会按照下面的q程在此文档中,h试工程团队成员~写代码始终很大Qƈ演示重复他或她真正获取谷歌测试的宗?/font>

发布q程

我们遵@颠覆Z目的典型释放过E:

  1. 创徏名ؓ释放 X.Y释放分支?/li>
  2. Bug 固定的树q?Q?d的功?/font>q些个别的修补程序合q到释放U才E_?/font>
  3. 个h单点发行?( Z?tt style="font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; max-width: 66em; ">X.Y.Z) 提出从分支创建标{?/li>
  4. 重复步骤 2 ?3Q整个一个发布周?Q如由功能或旉Q?/li>
  5. q回到步?1Q创建另一个版本分支,{等?/li>

此页Z?a rel="nofollow" target="_top" style="color: #0000cc; ">h Web 工具?/a>目指南?GWT 更好?/font>除了作ؓ?a rel="nofollow" target="_top" style="color: #0000cc; ">指出Q该|页的内容被许可创新知识׃n|名 2.5 许可?/a>.



RTY 2011-09-26 20:55 发表评论
]]>
V1_6_FAQ 提示和常见问题关于谷?c + + 试框架http://m.shnenglu.com/lauer3912/archive/2011/09/26/156884.htmlRTYRTYMon, 26 Sep 2011 12:53:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156884.htmlhttp://m.shnenglu.com/lauer3912/comments/156884.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156884.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/156884.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/156884.html阅读全文

RTY 2011-09-26 20:53 发表评论
]]>
V1_6_XcodeGuide ?Mac OS X ?Xcode 使用h试框架指南 http://m.shnenglu.com/lauer3912/archive/2011/09/26/156882.htmlRTYRTYMon, 26 Sep 2011 12:48:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156882.htmlhttp://m.shnenglu.com/lauer3912/comments/156882.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156882.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/156882.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/156882.html
搜烦 
 对于  
V1_6_XcodeGuide  
?Mac OS X ?Xcode 使用h试框架指南 
部v阶段
?a target="_top" style="color: #0000cc; white-space: nowrap; ">zhanyong...@gmail.com更新?span title="Mon Apr 18 14:45:17 2011">2011 q?4 ?18 ?/span>

本指南将介绍如何?Mac OS X 上的 Xcode 目中用谷歌测试框架?/font>本教E开始快速解释ؓ有经验的用户做什么?/font>快速v步后指南 ?都提供了q一步的解释每个步骤?/font>

快速入?/font>

q是快速指南在 Xcode 目中用谷歌测试?/p>

  1. 使用此命令在|站下蝲源代码: svn {և http://googletest.googlecode.com/svn/trunk/ googletest 只读
  2. 开放中?tt style="font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; max-width: 66em; ">gtest.xcodeproj googletest-??xcode/目录q生?gtest.framework?/li>
  3. 名ؓ"UnitTests"cM Xcode 目中创Z个新?壛_?目标
  4. ?gtest.framework d到您的项目,q将其添加到"UnitTests"?链接二进制与图书?生成阶段
  5. d您的单元试的源代码?UnitTests"?~译来源"生成阶段
  6. ~辑"UnitTests"可执行文件ƈd环境变量命名?DYLD_FRAMEWORK_PATH"值等于框架包?gtest.framework 相对于已~译的可执行文g的\径?/li>
  7. 生成q{
以下各节q一步解释每个深度,更详l地描述了如何完成它包括一些变化在上面列出的步骤?/font>

获取?/font>

目前Q此处讨论的 gtest.framework 标记的谷歌测试版本中不可用,只有在树qӀ?/font>解释在谷歌测?a rel="nofollow" target="_top" style="color: #0000cc; ">|站Q你可以得到代码从匿?SVN 使用此命令:

svn checkout http://googletest.googlecode.com/svn/trunk/ googletest-read-only

或者,如果您正在用颠覆您自己的代码库中,可以d Google Test 作ؓ外部相关性颠覆资料库?/font>按照q种ҎQ签?svn 存储库,所有h都还收C?Google 试 Q特定的版本Q如果您愿意Q?而不必显式签出?/font>q一l的l成目单ƈ减少复制到存储库中的代码?/font>

若要使用svn:externalsQ决定要具有外部源ȝ?/font>因ؓ你想要分支的一部分q行发布Ӟ您可以选择把树q内, 外部源?/font>然而,把它UCؓthird-party/googletest/1.0.1cM的版本标记的目录中的树干以外Q是另一U选择?/font>一旦徏立了位置Q则使用svn propedit svn:externals _directory_ svn:externals 属性设|对存储库中的目录?/font>此目录不能包含的代码Q但它的版本控制的父目录?/font>

命osvn propedit会出现你颠覆编辑器中,使编辑更单长Q(可能多行Q?的属性?/font>q同一U方法可用于查出标记的分支,通过使用相应?URL Q例?tt style="font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; max-width: 66em; ">http://googletest.googlecode.com/svn/tags/release-1.0.1Q?/font>另外Qsvn:externals 属性允许的特定修订版的树干与规?tt style="font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; max-width: 66em; ">-r_ # # _选项 Q例?tt style="font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', monospace; font-size: 12px; max-width: 66em; ">的外部对?src/googletest-r60 http://googletest.googlecode.com/svn/trunk).

q里是一个项?(阅读通过svn propget) 树干上?svn:externals 属性的CZ?/font>此值将{և到谷歌测试的副本d/外部对象/src/googletest/目录?/font>

[Computer:svn] user$ svn propget svn:externals trunk
externals
/src/googletest http://googletest.googlecode.com/svn/trunk

向项目中d框架

下一步是建立q将 gtest.framework d到您自己的项目?/font>本指南描qC下面的两U常用方法?/font>

  • 选项 1 ---谷歌测试添加到您自q目Q最单的Ҏ是打开 gtest.xcodeproj (xcode 中发现谷歌测试树q的目录Q?和手动构建框架?/font>然后Q将生成的框架添加到您的目使用"d-> 现有框架 … …"从上下文菜单?目-> d..."从主菜单?/font>Gtest.framework 是可重定位,q包含页眉和您需要进行测试的目标代码?/font>此方法要求重建每ơ您升h试您的目中?/font>
  • 选项 2 ---如果您打将为生的谷歌测试树qԌ其最新的功能U_您的单元试 Q或自己是谷歌测试开发h员)?/font>你要重徏框架Q每ơ更新源?/font>要这样做Q您需要将 gtest.xcodeproj 文gQ框架本w,d到您自己 Xcode 目?/font>然后Q从生成产品目的三角Ş发现Q您可以扑ֈ gtest.frameworkQ可以添加到你的目标 Q以下讨论)?/font>

使测试目?/font>

要开始编写测试,使新?壛_?目标?/font>此目标模板是 BSD?可可?或碳下可用?/font>d您的单元试的目?~译来源"生成阶段的源代码?/font>

接下来,你要在两U不同方法中d gtest.frameworkQ取决于哪个选项您选择以上?/p>

  • 选项 1 ---在编译期_Xcode 需要知道您要链接对 gtest.framework?/font>?gtest.framework d到您的测试目标的"链接二进制与图书?生成阶段?/font>q将包括h试头在标题搜烦路径中,q将告诉链接器在哪里可以扑ֈ的库?/font>
  • 选项 2 ---如果您工作的树干Q你也要?gtest.framework d到您的测试目标的?链接二进制与图书?生成阶段?/font>此外Q你要将 gtest.framework 作ؓ依赖Ҏ加到你的单元试目标?/font>q种方式QXcode 确保该 gtest.framework 是到目前为止Q每ơ您的构Z的目标?/font>最后,如果你不使用h试׃n生成目录Q你得将 gtest.framework 复制到您自己生成产品目录Q?q行脚本"生成阶段?/font>

讄可执行文件运行环?/font>

可执行单元测试是壳的工具Q因为它没有一捆,内容/框架的目录,在其中放|?gtest.framework?/font>相反Q必dq行时告诉动态链接器搜烦中的另一个位|的框架?/font>q可以通过?~辑zd的可执行文g..."中设|的"DYLD_FRAMEWORK_PATH"的环境变?/font>参数选项卡,?讄环境变量Q??/font>此值的路径是目录的包含 gtest.framework 的\?Q相Ҏl对Q?/font>

如果您还没有讄 DYLD_FRAMEWORK_PATHQ正,可能会得到这样一条消息:

[Session started at 2008-08-15 06:23:57 -0600.]
  dyld
: Library not loaded: @loader_path/../Frameworks/gtest.framework/Versions/A/gtest
   
Referenced from: /Users/username/Documents/Sandbox/gtestSample/build/Debug/WidgetFrameworkTest
   
Reason: image not found

要正这个问题,必须包含可执行文件中指定的目?从引用:"上述错误消息中的倹{?/font>然后Q在此位|的l端Q发现包?gtest.framework 的目录的相对路径?/font>q就是你需要设|ؓ DYLD_FRAMEWORK_PATH 的倹{?/font>

生成q{

现在Q当您单?生成?Q将会执行测试?/font>們ր出q样的事Q?/font>

[Session started at 2008-08-06 06:36:13 -0600.]
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from WidgetInitializerTest
[ RUN      ] WidgetInitializerTest.TestConstructor
[       OK ] WidgetInitializerTest.TestConstructor
[ RUN      ] WidgetInitializerTest.TestConversion
[       OK ] WidgetInitializerTest.TestConversion
[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran.
[  PASSED  ] 2 tests.

The Debugger has exited with status 0.  

摘要

单元试是有价值的方式Q确保你的数据模型保持有效,即在快速发展过E中或重构?/font>h试框架是伟大单元测试框ӞC ?c + + 于一体的?Xcode 开发环境很好?/font>



RTY 2011-09-26 20:48 发表评论
]]>
V1_6_AdvancedGuide 关于使用h c + + 试框架的高U的主题http://m.shnenglu.com/lauer3912/archive/2011/09/26/156881.htmlRTYRTYMon, 26 Sep 2011 12:41:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156881.htmlhttp://m.shnenglu.com/lauer3912/comments/156881.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156881.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/156881.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/156881.html阅读全文

RTY 2011-09-26 20:41 发表评论
]]>
V1_6_Samples h c + + 试框架hhttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156879.htmlRTYRTYMon, 26 Sep 2011 12:30:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156879.htmlhttp://m.shnenglu.com/lauer3912/comments/156879.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156879.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/156879.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/156879.html
搜烦 
 对于  
V1_6_Samples  
h c + + 试框架h
?a target="_top" style="color: #0000cc; white-space: nowrap; ">zhanyong...@gmail.com更新?span title="Mon Apr 18 14:45:17 2011">2011 q?4 ?18 ?/span>

如果你想和我们一P你想看一些谷歌测试示例代码?/font>Samples 文g?/a>h大量注释CZ演示了如何用各U谷歌测试功能?/font>

  • CZ # 1昄了用谷歌测试来试 c + + 函数的基本步骤?/li>
  • CZ # 2昄了一cd有多个成员函数更复杂的单元测试?/li>
  • CZ # 3使用试夹具?/li>
  • CZ # 4|于基准试夹具?׃n试逻辑和派生夹具中重新使用它?/li>
  • CZ # 5教如何通过从它z sub-fixtures 重用试夹具在多个测试用例?/li>
  • # 6 CZ演示cd参数试?/li>
  • CZ # 7教值参数测试的基本知识?/li>
  • CZ # 8昄值参数测试中使用Combine() ?/li>
  • CZ # 9昄了?API 修改 Google 试控制台输出的侦听器和查测试结果及其反?API 的用?/li>
  • CZ # 10昄侦听器的 API 来实现原始内存泄漏检器的用?/li>


RTY 2011-09-26 20:30 发表评论
]]>
V1_6_Primer 入门h c + + 试框架http://m.shnenglu.com/lauer3912/archive/2011/09/26/156878.htmlRTYRTYMon, 26 Sep 2011 12:28:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156878.htmlhttp://m.shnenglu.com/lauer3912/comments/156878.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/26/156878.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/156878.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/156878.html阅读全文

RTY 2011-09-26 20:28 发表评论
]]>
Google C++ Testing Framework?/title><link>http://m.shnenglu.com/lauer3912/archive/2011/09/19/156261.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Mon, 19 Sep 2011 13:03:00 GMT</pubDate><guid>http://m.shnenglu.com/lauer3912/archive/2011/09/19/156261.html</guid><wfw:comment>http://m.shnenglu.com/lauer3912/comments/156261.html</wfw:comment><comments>http://m.shnenglu.com/lauer3912/archive/2011/09/19/156261.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/lauer3912/comments/commentRss/156261.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/lauer3912/services/trackbacks/156261.html</trackback:ping><description><![CDATA[<span id="lprvzhp" class="Apple-style-span" style="font-family: verdana, sans-serif; font-size: 12px; line-height: 18px; background-color: #f8fafd; "><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>介:</strong> Google 提供一U用于ؓ C/C++ 软g开发单元测试的开放源码框Ӟ它很有意思,也很Ҏ使用。本文介l一些比较有用的 Google C++ Testing Framework Ҏ。本文基?version 1.4?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>Z么要使用 Google C++ Testing FrameworkQ?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">使用q个框架有许多好理由。本节讨论其中几个?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">某些cd的测试有p糕的内存问题,q些问题只在某几ơ运行期间出现。Google 的测试框架ؓ处理q种情况提供了出色的支持。可以?Google 框架重复q行相同的测试一千次。当出现故障的迹象时Q自动地调用调试器。另外,q只需要在命o行上传递两个开兛_可实玎ͼ--gtest_repeat=1000 --gtest_break_on_failure?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">与其他许多测试框架相反,可以?Google 试框架内置的断a部v在禁用了异常处理Q通常׃性能原因Q的软g中。因此,也可以在析构函数中安全地使用断言?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">q行试很简单。只需调用预定义的 RUN_ALL_TESTS 宏,而不需要通过创徏或驱动单独的q行器类来执行测试。这?CppUnit {框架方便多了?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">只需传递一个开兛_可生?Extensible Markup Language (XML) 报告Q?--gtest_output="xml:<file name>"。在 CppUnit ?CppTest {框架中Q需要编写很多代码才能生?XML 输出?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">创徏基本试</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">误虑 清单 1 所C的qx根函数的原型?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 1. qx根函数的原型</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">double square-root (const double);</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">对于负数Q这个例E返?-1。正数测试和负数试对它都有意义Q所以我们编写两个测试。清?2 l出试用例?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 2. qx根函数的单元试</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">#include "gtest/gtest.h"</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST (SquareRootTest, PositiveNos) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (18.0, square-root (324.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (25.4, square-root (645.16));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (50.3321, square-root (2533.310224));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST (SquareRootTest, ZeroAndNegativeNos) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EQ (0.0, square-root (0.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EQ (-1, square-root (-22.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">清单 2 创徏一个名?SquareRootTest 的测试层ơ结构,然后在其中添加两个单元测?PositiveNos ?ZeroAndNegativeNos。TEST 是在 gtest.hQ可以在下蝲的源代码中找刎ͼ中定义的预定义宏Q它帮助定义q个层次l构。EXPECT_EQ ?ASSERT_EQ 也是?— 对于前者,即出现试p|Q测试仍然l执行;对于后者,试l止执行。显Ӟ如果 0 的^Ҏ是除 0 之外的Q何数字,没必要再执行测试了。因此,ZeroAndNegativeNos 试只?ASSERT_EQQ?PositiveNos 试使用 EXPECT_EQQ来报告存在多少个测试用例是qx根函数失败而不l止试的情c?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>q行W一个测?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">既然已经创徏了第一个基本测试,现在pq行它了。清?3 是运行测试的ME?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 3. q行qxҎ?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">#include "gtest/gtest.h"</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST(SquareRootTest, PositiveNos) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (18.0, square-root (324.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (25.4, square-root (645.16));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (50.3321, square-root (2533.310224));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST (SquareRootTest, ZeroAndNegativeNos) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EQ (0.0, square-root (0.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EQ (-1, square-root (-22.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">int main(int argc, char **argv) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">::testing::InitGoogleTest(&argc, argv);</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">return RUN_ALL_TESTS();</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">֐思义Q?:testing::InitGoogleTest Ҏ的作用就是对框架q行初始化,必须在调?RUN_ALL_TESTS 之前调用它。在代码中只能调?RUN_ALL_TESTS 一ơ,因ؓ多次调用会与框架的一些高U特性冲H,不支持这U做法。注意,RUN_ALL_TESTS 自动地探ƈq行?TEST 宏定义的所有测试。在默认情况下,l果输出到标准输出。清?4 l出输出?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 4. q行qxҎ试的输出</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Running main() from user_main.cpp</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[==========] Running 2 tests from 1 test case.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] Global test environment set-up.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 2 tests from SquareRootTest</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ RUN ] SquareRootTest.PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">..\user_sqrt.cpp(6862): error: Value of: sqrt (2533.310224)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Actual: 50.332</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Expected: 50.3321</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] SquareRootTest.PositiveNos (9 ms)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ RUN ] SquareRootTest.ZeroAndNegativeNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ OK ] SquareRootTest.ZeroAndNegativeNos (0 ms)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 2 tests from SquareRootTest (0 ms total)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] Global test environment tear-down</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[==========] 2 tests from 1 test case ran. (10 ms total)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ PASSED ] 1 test.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] 1 test, listed below:</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] SquareRootTest.PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">1 FAILED TEST</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>Google C++ Testing Framework 的选项</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">?清单 3 中,InitGoogleTest 函数接受试基础设施的参数。本节讨论可以通过试框架的参数实现的一些功能?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">通过在命令行上传?--gtest_output="xml:report.xml"Q可以把输出转储?XML 格式。当Ӟ可以?report.xml 替换为您喜欢的Q何文件名?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">某些试有时候会p|Q但是在大多数时候会利通过。这是与内存损坏相关的问题的典型特点。如果多ơ运行测试,p够提高发现失败的可能性。如果在命o行上传?--gtest_repeat=2 --gtest_break_on_failureQ就重复q行相同的测试两ơ。如果测试失败,会自动调用调试器?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">q不需要每ơ都q行所有测试,其是在修改的代码只影响某几个模块的情况下。ؓ了支持运行一部分试QGoogle 提供 --gtest_filter=<test string>。test string 的格式是由冒?(:) 分隔的一pd通配W模式。例如,--gtest_filter=* q行所有测试,?--gtest_filter=SquareRoot* 只运?SquareRootTest 试。如果希望只q行 SquareRootTest 中的正数单元试Q应该?--gtest_filter=SquareRootTest.*-SquareRootTest.Zero*。注意,SquareRootTest.* 表示属于 SquareRootTest 的所有测试,?-SquareRootTest.Zero* 表示不运行名UC Zero 开头的试?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">清单 5 中的CZ?gtest_output、gtest_repeat ?gtest_filter 选项q行 SquareRootTest?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 5. ?gtest_output、gtest_repeat ?gtest_filter 选项q行 SquareRootTest</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[arpan@tintin] ./test_executable --gtest_output="xml:report.xml" --gtest_repeat=2 --</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">gtest_filter=SquareRootTest.*-SquareRootTest.Zero*</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Repeating all tests (iteration 1) . . .</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Note: Google Test filter = SquareRootTest.*-SquareRootTest.Z*</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[==========] Running 1 test from 1 test case.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] Global test environment set-up.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 1 test from SquareRootTest</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ RUN ] SquareRootTest.PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">..\user_sqrt.cpp (6854): error: Value of: sqrt (2533.310224)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Actual: 50.332</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Expected: 50.3321</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] SquareRootTest.PositiveNos (2 ms)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 1 test from SquareRootTest (2 ms total)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] Global test environment tear-down</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[==========] 1 test from 1 test case ran. (20 ms total)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ PASSED ] 0 tests.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] 1 test, listed below:</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] SquareRootTest.PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">1 FAILED TEST</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Repeating all tests (iteration 2) . . .</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Note: Google Test filter = SquareRootTest.*-SquareRootTest.Z*</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[==========] Running 1 test from 1 test case.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] Global test environment set-up.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 1 test from SquareRootTest</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ RUN ] SquareRootTest.PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">..\user_sqrt.cpp (6854): error: Value of: sqrt (2533.310224)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Actual: 50.332</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Expected: 50.3321</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] SquareRootTest.PositiveNos (2 ms)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 1 test from SquareRootTest (2 ms total)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] Global test environment tear-down</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[==========] 1 test from 1 test case ran. (20 ms total)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ PASSED ] 0 tests.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] 1 test, listed below:</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] SquareRootTest.PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">1 FAILED TEST</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>临时用试</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">可以临时用试吗?可以Q只需在逻辑试名或单元试名前面加?DISABLE_ 前缀Q它׃会执行了。清?6 演示如何用 清单 2 中的 PositiveNos 试?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 6. 临时用试</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">#include "gtest/gtest.h"</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST (DISABLE_SquareRootTest, PositiveNos) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (18.0, square-root (324.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (25.4, square-root (645.16));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (50.3321, square-root (2533.310224));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">OR</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST (SquareRootTest, DISABLE_PositiveNos) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (18.0, square-root (324.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (25.4, square-root (645.16));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_EQ (50.3321, square-root (2533.310224));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">注意Q如果禁用了M试QGoogle 框架会在试执行l束时输告消息,?清单 7?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 7. Google 警告用户在框架中有禁用的试</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">1 FAILED TEST</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">YOU HAVE 1 DISABLED TEST</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">如果希望l箋q行用的测试,那么在命令行上传?-gtest_also_run_disabled_tests 选项。清?8 l出q行 DISABLE_PositiveNos 试时的输出?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 8. Google 允许q行已经用的测?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 1 test from DISABLED_SquareRootTest</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ RUN ] DISABLED_SquareRootTest.PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">..\user_sqrt.cpp(6854): error: Value of: square-root (2533.310224)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Actual: 50.332</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Expected: 50.3321</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] DISABLED_SquareRootTest.PositiveNos (2 ms)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[----------] 1 test from DISABLED_SquareRootTest (2 ms total)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] 1 tests, listed below:</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">[ FAILED ] SquareRootTest. PositiveNos</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>断言</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Google 试框架提供一整套预定义的断言。断a分ؓ两类 — 名称?ASSERT_ 开头的断言和名UC EXPECT_ 开头的断言。如果一个断ap|QASSERT_* 断言会终止程序执行,?EXPECT_* 断言l箋q行。当断言p|Ӟq两者都输出文g名、行号和可定制的消息。比较简单的断言包括 ASSERT_TRUE (condition) ?ASSERT_NE (val1, val2)。前者期望条件L成立Q而后者期望两个g匚w。这些断a也适用于用户定义的cdQ但是必覆盖相应的比较操作W(==?=?lt;= {等Q?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>点数比?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Google 提供 清单 9 所C的宏以支持点数比较?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 9. 用于点数比较的?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_FLOAT_EQ (expected, actual)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_DOUBLE_EQ (expected, actual)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_NEAR (expected, actual, absolute_range)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_FLOAT_EQ (expected, actual)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_DOUBLE_EQ (expected, actual)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">EXPECT_NEAR (expected, actual, absolute_range)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Z么需要用单独的宏q行点数比较?使用 ASSERT_EQ 不行吗?使用 ASSERT_EQ 和相关的宏可能可以,也可能不行,但是使用专门用于点数比较的宏更好。通常Q不同的中央处理单元 (CPU) 和操作环境以不同的方式存储QҎQ简单地比较期望值和实际值是无效的。例如,ASSERT_FLOAT_EQ (2.00001, 2.000011) 会顺利通过 — 如果直到数点后四位都匹配,Google ׃会抛出错误。如果需要更_的比较,应该使用 ASSERT_NEAR (2.00001, 2.000011, 0.0000001)Q就会得?清单 10 所C的错误?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 10. ASSERT_NEAR 产生的错误消?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Math.cc(68): error: The difference between 2.00001 and 2.000011 is 1e-006, which exceeds</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">0.0000001, where</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">2.00001 evaluates to 2.00001,</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">2.000011 evaluates to 2.00001, and</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">0.0000001 evaluates to 1e-007.</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>M试</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Google C++ Testing Framework 有一cL的断言QASSERT_DEATH、ASSERT_EXIT {)Q它们称为死亡断a。用这U断a查在例程的输入错误的情况下是否发出正的错误消息Q或者例E是否退出ƈq回正确的退出码。例如,?清单 3 中,当执?square-root (-22.0) 时应该生错误消息,E序退出,q回状态应该是 -1 而不?-1.0。清?11 使用 ASSERT_EXIT 查是否是q样?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 11. 使用 Google 框架q行M试</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">#include "gtest/gtest.h"</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">double square-root (double num) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">if (num < 0.0) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">std::cerr << "Error: Negative Input\n";</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">exit(-1);</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">// Code for 0 and +ve numbers follow…</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST (SquareRootTest, ZeroAndNegativeNos) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EQ (0.0, square-root (0.0));</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EXIT (square-root (-22.0), ::testing::ExitedWithCode(-1), "Error:</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Negative Input");</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">int main(int argc, char **argv) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">::testing::InitGoogleTest(&argc, argv);</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">return RUN_ALL_TESTS();</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EXIT 查函数是否退出ƈq回正确的退出码Q即 exit ?_exit 例程的参敎ͼQ对引号中的字符串和函数发送到标准错误的字W串q行比较。注意,错误消息必须发送到 std::cerr 而不?std::cout。清?12 l出 ASSERT_DEATH ?ASSERT_EXIT 的原型?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 12. M断言的原?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_DEATH(statement, expected_message)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">ASSERT_EXIT(statement, predicate, expected_message)</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Google 提供预定义的谓词 ::testing::ExitedWithCode(exit_code)。只有在E序退Z退出码与谓词中?exit_code 相同的情况下Q这个谓词的l果才是 true。ASSERT_DEATH ?ASSERT_EXIT 单;它只查标准错误中的错误消息是否是用户期望的消息?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>理解试装备</strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">在执行单元测试之前,通常要执行一些定制的初始化。例如,如果希望度量试的时?内存占用量,需要放|一些测试专用代码以度量q些倹{这是装备的用?— 它们帮助完成q种定制的测试初始化。清?13 l出一个装备类的示例?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 13. 试装备c?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">class myTestFixture1: public ::testing::test {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">public:</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">myTestFixture1( ) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">// initialization code here</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">void SetUp( ) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">// code here will execute just before the test ensues</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">void TearDown( ) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">// code here will be called just after the test completes</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">// ok to through exceptions from here if need be</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">~myTestFixture1( ) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">// cleanup any pending stuff, but no exceptions allowed</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">// put in any custom data members that you need</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">};</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">q个装备cL生自 gtest.h 中声明的 ::testing::test cR清?14 是用这个装备类的示例。注意,它?TEST_F 宏而不?TEST?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>清单 14. 装备的用示?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST_F (myTestFixture1, UnitTest1) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">….</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">TEST_F (myTestFixture1, UnitTest2) {</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">….</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">}</p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">在用装备时Q要注意以下几点Q?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">可以在构造函数或 SetUp Ҏ中执行初始化或分配资源。由用户选择具体方式?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">可以?TearDown 或析构函CE中释放资源。但是,如果需要异常处理,那么只能?TearDown 代码中进行,因ؓ从析构函C抛出异常会导致不定的结果?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">在以后的版本中,Google 断言宏可能会在^C抛出异常。因此,Z便于l护Q最好在 TearDown 代码中用断a宏?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">q不跨多个测试用同一个测试装备。对于每个新的单元测试,框架创徏一个新的测试装备。在 清单 14 中,׃要创Z?myFixture1 对象Q所以两ơ调?SetUp 例程Q请注意使用正确的拼写)?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>l束?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">本文仅仅触及?Google C++ Testing Framework 的皮毛。可以从 Google |站获得关于q个框架的详l信息。对于高U开发h员,我徏议阅?Boost 单元试框架?CppUnit {开攑֛归框架方面的其他文章。更多信息参?参考资料?/p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; "><strong>关于作?/strong></p><p style="font-size: 14px; line-height: 25px; text-indent: 25px; ">Arpan Sen 是致力于电子设计自动化行业的软g开发首席工E师。他使用各种 UNIX 版本Q包?Solaris、SunOS、HP-UX、IRIXQ以?Linux ?Microsoft WindowsQ已l多q。他热衷于各UY件性能优化技术、图论和q行计算。Arpan 获得了Y件系l硕士学位?/p></span><img src ="http://m.shnenglu.com/lauer3912/aggbug/156261.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/lauer3912/" target="_blank">RTY</a> 2011-09-19 21:03 <a href="http://m.shnenglu.com/lauer3912/archive/2011/09/19/156261.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google Test Primer Q开始用Google C++ 试框架http://m.shnenglu.com/lauer3912/archive/2011/09/16/155965.htmlRTYRTYFri, 16 Sep 2011 13:59:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/16/155965.htmlhttp://m.shnenglu.com/lauer3912/comments/155965.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/16/155965.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/155965.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/155965.html阅读全文

RTY 2011-09-16 21:59 发表评论
]]>
E序员的共鸣 - 诅R卓有成效的E序员?/title><link>http://m.shnenglu.com/lauer3912/archive/2011/09/15/155812.html</link><dc:creator>RTY</dc:creator><author>RTY</author><pubDate>Wed, 14 Sep 2011 23:36:00 GMT</pubDate><guid>http://m.shnenglu.com/lauer3912/archive/2011/09/15/155812.html</guid><wfw:comment>http://m.shnenglu.com/lauer3912/comments/155812.html</wfw:comment><comments>http://m.shnenglu.com/lauer3912/archive/2011/09/15/155812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/lauer3912/comments/commentRss/155812.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/lauer3912/services/trackbacks/155812.html</trackback:ping><description><![CDATA[<span id="zvplxnn" class="Apple-style-span" style="color: #333333; font-family: 微Y雅黑, verdana, Arial, sans-serif; background-color: #ffffff; "><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">最q读了《卓有成效的E序员》,感觉收获颇大。这是一本写l程序员的难得的好书。书中大都是一些浅昄道理Q但作者将q些东西加以攉、归U뀁ȝQƈ最l成书。作者ؓ了收集各U提高效率的工具和方法,东奔西走Q可谓费了一番苦心?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">我觉得此书第一部分ȝ的一些法则非常好Q我提取了一下:<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">法则Q?nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h3><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">1.加速法?/h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    x本质Q而非形式</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    一个应用程序列表的有用E度与它的长度成反比 <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">E序员的很多旉都浪费在找东西上</span> <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    华而不实的东西中看不中?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    键盘输入LD?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">首选键盘而非鼠标</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    地址栏是Windows资源理器界面中最高效的部?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">q旉来学习你手边的所有隐藏的快捷?/span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    环境切换会消耗时?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">成批复制_脓要比反复多次复制_脓?/span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    忘记历史意味着你得再输入一?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    嵌入囑Ş化工L命o提示W让你鱼与熊掌兼?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    在上下文中学习IDE快捷键,而不要去背长长的列表</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    当你W二ơ输入一个复杂结构时Q将它做成模?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    如果要对多行文本做同L操作Q就应该扑և其中的模式,q把它记录ؓ一个宏</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">不要L重复输入相同的命?/span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    每天׃点点旉来每一天都更高?nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">2.专注法则</h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">_֊集中,思维缜?/span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    排除q扰Q隔ȝ略,x不需要的提示Q创造安静时?nbsp; <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">草堆大Q从中找C栚wp?/span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    不要问文件树Q要搜烦</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    使用多显C器</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    虚拟桌面可以让原本杂乱无章的一大堆H口变得整洁 <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">3.自动化法?/h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    不要重新发明轮子</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    用Selenium览|页</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    不要费旉动手d可以被自动化的事?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    用Windows Power Shell替代批处理文?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    驯服Subversion命o?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    以创造性的方式解决问题Q有助于在将来解决类似的问题</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">是否应该自动化的关键在于投资回报率和~解风险</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    研究性的工作应该攑֜旉盒里?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    别给牦牛剪毛 <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">4.规范性法?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    对于M你不自己L建的东西Q只在版本控制中保存一份副?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    使用标准的构建服务器</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">通过复制_脓来复用是邪恶的,不论你复制粘贴的是什?/span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    利用虚拟q_佉K目依赖标准化</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    不要让对?- 关系映射工具QO/R映射器)q反规范原则 <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    通过扩展。开攄Qopen classQ,或者部分类Qpartial classQ?来ؓ生成的代码增加行?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    始终保持代码和数据结构的同步</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">q时的文档比没有文档更糟Q因为它会主动误g</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    M需要费劲创造的东西Q都让它的创造者欲|不?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">白板 + 数码相机MCASE工具</span></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    量生成所有技术文?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    <span style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; line-height: 1.5; color: red; ">重复是Y件开发中最大的d </span><br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">工具Q?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h3><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">书中Q还提到了大量的提高效率的工P都是非常不错的。相信很多h都有自己的一个列表,下面是我电脑中必不可的几款软gQ?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    1. FireFox 及其各类插g<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    2. Launchy启动加速器</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    3. Total Commander</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    4. ClipX多重剪切?/p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    5. EmEditor文本~辑?nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    6. Vistual Studio的VA插g</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    7. Search And Replace</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    8. Everything</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    9. Miranda IM<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">    10. .... <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">感触Q?nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h3><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">1. 愤怒的猴子 <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">在书中的W二部分Q提C很多实践相关的内宏V让我感触最q?#8220;愤怒的猴子”的故事:</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">“<em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">早在20世纪60q代Q那时候科学家们可以做M疯狂的事情)Q行为科学家们进行了一实验。他们把五只猴子和一架活梯放在一间屋子里Qƈ在天花板上挂了一串香蕉。这些猴子很快就惛_它们可以爬上梯子d香蕉Q但每当它们靠近zL的时候,U学家们q冰水满整个屋子。我想你能猜C发生什么:一愤怒的猴子。很快,再没有一只猴子会去靠q那个梯子了?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></em></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">之后Q科学家们将其中一只猴子替换成另一只没有忍受过冰水折磨的新猴子。这只新猴子所做的W一件事是直奔那架梯子Q但当它q么做时其他所有猴子都痛打它。它不明白ؓ什么,但很快就学乖了:不要去靠q那架梯子。科学家们逐渐最初的那些猴子都替换成新猴子,直到q群猴子中谁都没有被水浸泡过Q然而它们还是会LMQ何靠q梯子的猴子?/em></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; "><em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">q说明了什么?软g目中许多惯例之所以存在,因?#8221;我们一直是那样做的“。换句话_是因为愤怒的猴子?/em>”</p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">我们组在制定C++相关的代码规范时遇到过无数cM的问题。比如,在制定变量的命名规范Ӟ我们针对是否采用匈牙利命名法争论了很久。有的h认ؓQ?几乎以前看到的所有C++代码都采用了匈牙利命名法Q甚臻I微Y定义的所有API都用了此类命名法。刚开始,我也是有同样的疑惑?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">后来Q我们经q仔l分析C++匈牙利命名法由来Q渐渐感觉我们就是那些愤怒的猴子Q盲目跟从前人的方式Q缺乏打破传l的勇气。C++有着其特D的历史原因Q很多标准一直沉淀下来q很改变。我们再看看后来新生的那些编E语aQC#, Python…… 都抛弃了匈牙利命名法Q同时再看看现在C++前沿的C++ 0x以及现在出版的一些书中,也渐渐放弃了对匈牙利命名法的使用。因为类型的意义在对象模型中来弱化。因此,最后我们放弃了匈牙利命名法q个老古董?nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">2. 敏捷开?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">q本书带有强烈的ThoughtWorks色彩Q敏L思想贯穿全书Q包括测试驱动设计,白板Q结对编E。这也让我对敏捷产生了更加强烈的兴趣?其中有一D|试驱动开发TDD的一D|事:<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">“<em style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">记得W一ơ和一些已l习惯于单元试的开发h员一起动手开始修改代码时Q我也是非常紧张Q因为大量的修改往往会破坏很多东西,但他们看h丝毫没有犹U。逐渐圎ͼ我也放下心来Q因为我慢慢地认识到Q有了测试的保证Q完全可以放心大胆地M改代码?/em>” <br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h4 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">3. 有趣的故?nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></h4><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">书中q有一些有的故事Q比如作者的一个朋友在和别人结对编E时Qؓ了养成同伴用快捷键的习惯,每当同伴未用快捷键Ӟ他都会要求将操作撤销Q然后要求用快捷键再重复操?ơ。然后,在其凶狠的眼中Q同伴很快掌握了快捷键?nbsp;<br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " /></p><h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; font-weight: bold; color: #000000; ">ȝQ?/h3><p style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">q本书很薄,蕴藏的道理却不少Q相信每个读q它的h都会从中收获。读q之后,我们不应该局限于书中提到的某些小技巧, 或是书中某一个细节,毕竟Q提供效率的Ҏ有很多很多,法则也有很多很多Q一本书很难其ID完。我们应该从书中吸取其思想Qƈ在实际工作和学习中不断ȝQ做一个真正的“卓有成效的程序员”Q?/p></span><img src ="http://m.shnenglu.com/lauer3912/aggbug/155812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/lauer3912/" target="_blank">RTY</a> 2011-09-15 07:36 <a href="http://m.shnenglu.com/lauer3912/archive/2011/09/15/155812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google Test 架构http://m.shnenglu.com/lauer3912/archive/2011/09/15/155807.htmlRTYRTYWed, 14 Sep 2011 22:40:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/15/155807.htmlhttp://m.shnenglu.com/lauer3912/comments/155807.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/15/155807.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/155807.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/155807.htmlhttp://code.google.com/p/googletest/

引文Q?a >http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html

玩{Google开源C++单元试框架Google Testpd(gtest)(?

前段旉学习和了解了下Google的开源C++单元试框架Google TestQ简UgtestQ非常的不错?我们原来使用的是自己实现的一套单元测试框Ӟ在用过E中Q发现越来越多用不便之处,而这样不便之处,gtest恰恰很好的解决了?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " />

其实gtest本n的实现ƈ不复杂,我们完全可以模仿gtestQ不断的完善我们的测试框Ӟ 但最后我们还是决定用gtest取代掉原来的自己的测试框Ӟ原因是:

1.不断完善我们的测试框架之后就会发觉相当于把gtest重新做了一遍,虽然轮子造的很爽Q但是不是必要的?/p>

2.使用gtest可以免去l护试框架的麻烦,让我们有更多_֊投入到案例设计上?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " />

3.gtest提高了非常完善的功能Qƈ且简单易用,极大的提高了~写试案例的效率?/p>

gtest的官方网站是Q?/p>

http://code.google.com/p/googletest/

从官方的使用文档里,你几乎可以获得你惌的所有东?nbsp;

http://code.google.com/p/googletest/wiki/GoogleTestPrimer

http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide 

如果q想对gtest内部探个I竟Q就把它的代码下载下来研I吧Q这是开源的好处Q哈Q?nbsp;

官方已经有如此完备的文档了,Z么我q要写呢Q一斚w是自p记笔讎ͼ好记性不如烂W头Q以后自己想查查一些用法也可以直接在这里查刎ͼ一斚w是对于不惛_看一大堆英文文档的朋友,在我q里可以快速的扑ֈgtest相关的内宏V?/p>

下面是该pd的目录:

1.玩{Google开源C++单元试框架Google Testpd(gtest)之一 - 初识gtest

2.玩{Google开源C++单元试框架Google Testpd(gtest)之二 - 断言

3.玩{Google开源C++单元试框架Google Testpd(gtest)之三 - 事g机制

4.玩{Google开源C++单元试框架Google Testpd(gtest)之四 - 参数?/a>

5.玩{Google开源C++单元试框架Google Testpd(gtest)之五 - M试 

6.玩{Google开源C++单元试框架Google Testpd(gtest)之六 - q行参数

7.玩{Google开源C++单元试框架Google Testpd(gtest)之七 - 深入解析gtest

8.玩{Google开源C++单元试框架Google Testpd(gtest)之八 - 打造自q单元试框架


额外:

1.gtest中如何蟩出当前测试案?/a>

2.~写优美的GTest试案例

3.gtest 参数化测试代码示?/a> (内含完整工程CZ)

作者:CoderZhQ?a target="_blank" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">CoderZh的技术博?- 博客?/a>Q?br style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; " />微博Q?a target="_blank" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">http://t.sina.com.cn/coderzh 
出处Q?a target="_blank" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; text-decoration: none; color: #0066aa; border-bottom-width: 0px; border-bottom-style: initial; border-bottom-color: initial; ">http://coderzh.cnblogs.com
文章版权归本人所有,Ƣ迎转蝲Q但未经作者同意必M留此D声明,且在文章面明显位置l出原文q接Q否则保留追I法律责ȝ权利?/p>




RTY 2011-09-15 06:40 发表评论
]]>
谈谈单元试http://m.shnenglu.com/lauer3912/archive/2011/09/15/155806.htmlRTYRTYWed, 14 Sep 2011 22:37:00 GMThttp://m.shnenglu.com/lauer3912/archive/2011/09/15/155806.htmlhttp://m.shnenglu.com/lauer3912/comments/155806.htmlhttp://m.shnenglu.com/lauer3912/archive/2011/09/15/155806.html#Feedback0http://m.shnenglu.com/lauer3912/comments/commentRss/155806.htmlhttp://m.shnenglu.com/lauer3912/services/trackbacks/155806.html

刚接触单元测试时Q我实在是搞不懂q种试到底有多大的意义Q无非都是一些简单的ASSERTQ但q年U篏一些经验教训之后我才发玎ͼ单元试q玩意儿真的是一U保证程序质量的强有力手Dc?/p>

Z么这栯QD个最典型的例子,无论是开发新功能q是l护老程序,都不可避免的要反复修Ҏ些代码,那该怎么保证修改的代码不会引入新的问题?如果你说你用人品担保Q那我服了。我们应该需要一U可靠的手段来保证修改一个BUG不会引入两个三个BUGQ或者不会让之前正确的功能出错,甚至是不会引入之前已l修改过的其它BUG。测试无疑是一U很好的手段Q在没掌握其它更好的Ҏ之前Q很多h喜欢用h工测试,即编?– q行 – 输入典型?– 看程序运行结?– 如果出错 – 修改后再~译……从长q来看,q种试Ҏ的缺点很明显Q?、耗时耗力Q每ơ修改代码后都需要h工重复测试所有的典型情况Q?、容易出错、漏,太复杂,你很难记得几个月前的那个BUG到底是什么情况,因此没法试Q久而久之,N久前的那个BUG可能又神不知g觉的重现了。相比之下,单元试的优点就凸显出来了,把需要测试的典型情况~写为测试代码,然后~译q行卛_完成自动化的试Q当发现BUG后,把它d到测试用例,不仅可以提高试覆盖率,q能保证以后不会再次引入同样的BUGQ有了够的单元试Q覆盖率Q,你就可以理直气壮的说代码没问题!当然引入单元试会在前期p不少的时间来~写试代码Q但相应的也会大大减后期的l护工作Q最主要的是能可靠的提高E序的质量,所以是值得的,甚至是必要的。(如果你编译过开源的Google Chromium览器,你会发现试代码占了不,光是~译试代码得花很长的旉Q?/p>

除了上面说的优点外,单元试q有一些其它的好处Q比如帮你理清设计。一些h反对单元试是因ؓ说我们的应用太复杂了Q没法做单元试。其实不一定是应用复杂Q而有可能是设计得有问题,D了没有可试性。比如有些h喜欢在CXXXDialog里编写所有的功能实现Q要试q样的代码,必须得创建出DialogQ可能还需要点击,q显然没法做单元试Q但如果把Dialog和业务逻辑分开设计Q那臛_业务逻辑是可试的(暂不讨论UI的测试)。因此要做单元测试,得设计好各个接口,保证某个接口只完成单一的功能,没有q多的耦合依赖?/p>

前面只是泛泛而谈了一些单元测试的皮毛Q鼓吹了一些优点,有兴的自己找更详细的资料看吧。另推荐一个Google的开源C++试框架googletest?/p>

RTY 2011-09-15 06:37 发表评论
]]>
69SEXþþƷ鶹| ޾Ʒþþþþþþþþþ| ޾Ʒҹvaþ| 99þùۺϾƷԭ| þþƷ99þþ| 뾫Ʒþþþ..| ھƷۺϾþþ40p| 777ҹƷþav| ޹ƷþSM| ɫۺϾþ| ˾þþƷavһ| | þþþѿӰƬ| þþƷž޾Ʒ| ձþ| ɫþˬˬƬaV | ޾ƷNVþþþþþþþ| þ99Ʒ99þ6| þ޾Ʒҳ| 鶹þþ9ԴƬ| 77777ҹþö| Ʒþþþù| ޾Ʒһþ| ݹ97þ÷ѹۿ| һþaþþƷvrۺ| .Ʒþþ鶹Ʒ| 91Ʒɫ۾þ| ݺɫþۺͼƬ | þþþAVרվ | þӰۺ| Ʒþþþþ| Avþ| þþþùɫAVѿͼƬ| þþһƷ99þþƷ88| Ʒþþþþù| Ļþþ| 99þþƷһ| ɫۺϾþþþ| ˳þõӰվ| ˾ƷþѶ| þþƷԴվ|