﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-O'O.OOO'</title><link>http://www.cppblog.com/giftedchen/</link><description>美丽的小圈圈</description><language>zh-cn</language><lastBuildDate>Tue, 09 Jun 2026 20:02:15 GMT</lastBuildDate><pubDate>Tue, 09 Jun 2026 20:02:15 GMT</pubDate><ttl>60</ttl><item><title>&lt;转载&gt;ACE的一个调试经过</title><link>http://www.cppblog.com/giftedchen/archive/2008/08/06/58122.html</link><dc:creator>搞搞陈</dc:creator><author>搞搞陈</author><pubDate>Wed, 06 Aug 2008 03:37:00 GMT</pubDate><guid>http://www.cppblog.com/giftedchen/archive/2008/08/06/58122.html</guid><wfw:comment>http://www.cppblog.com/giftedchen/comments/58122.html</wfw:comment><comments>http://www.cppblog.com/giftedchen/archive/2008/08/06/58122.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/giftedchen/comments/commentRss/58122.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/giftedchen/services/trackbacks/58122.html</trackback:ping><description><![CDATA[<br>
／＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊<br>
＊　　　　　　　　　　　　　　版权声明<br>
＊　　　本文为本人原创，本人拥有此文的版权。鉴于本人持续受益于开源软件社区，<br>
＊　本人声明：任何个人及团体均可不受限制的转载和复制本文，无论是否用于盈利<br>
＊　之目的，但必须在转载及复制时同时保留本版权声明，否则为侵权行为，本人保<br>
＊　留追究相应法律责任之权利。<br>
＊　　　　　　　　　　　　　　&nbsp;&nbsp; speng2005@gmail.com<br>
＊　　　　　　　　　　　　　　&nbsp;&nbsp; 　　&nbsp;&nbsp; 2007-12<br>
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊／ &nbsp;<br>
<br>
&nbsp;&nbsp;&nbsp; 近日在使用ACE进行开发的工作中遇到一个导致程序崩溃的问题。我们是在Linux平台上使用ACE 5.5.1以及g++ 4.1.2进行开发的。<br>
&nbsp;&nbsp;&nbsp;
问题是这样的：我们的程序中有多个线程并发调用ACE_Log_Msg::log（）方法进行日志输出操作，这些日志将被输出到同一个位于本地磁盘上的日
志文件中。但为防止日志文件长度增长过大，我们使用了ACE_Logging_Strategy类来进行定时的日志文件大小检查和文件切换。这种方式在一
般情况下运行良好，但是在大规模压力测试中，程序常常因访问非法内存而崩溃。发生崩溃时的调用栈总是这样的：<br>
<br>
<span style="color: #009902;">(gdb) bt</span><br style="color: #009902;">
<span style="color: #009902;">#0&nbsp; 0xb7c1bb4a in memcpy () from /lib/tls/i686/cmov/libc.so.6</span><br style="color: #009902;">
<span style="color: #009902;">#1&nbsp; 0xb7d9c006 in std::basic_streambuf&lt;char, std::char_traits&lt;char&gt; &gt;::xsputn () from /usr/lib/libstdc++.so.6</span><br style="color: #009902;">
<span style="color: #009902;">#2&nbsp; 0xb7d66d59 in std::basic_filebuf&lt;char, std::char_traits&lt;char&gt; &gt;::xsputn () from /usr/lib/libstdc++.so.6</span><br style="color: #009902;">
<span style="color: #009902;">#3&nbsp; 0xb7d911df in std::operator&lt;&lt; &lt;std::char_traits&lt;char&gt; &gt; () from /usr/lib/libstdc++.so.6</span><br style="color: #009902;">
<span style="color: #009902;">#4&nbsp; 0xb7ed8f15 in ACE_Log_Record::print (this=0x2befe228, host_name=0x0, verbose_flag=4, s=@0x80521c8) at Log_Record.cpp:302</span><br style="color: #009902;">
<span style="color: #009902;">#5&nbsp; 0xb7ed70a6 in ACE_Log_Msg::log (this=0x8588a58, log_record=@0x2befe228, suppress_stderr=0) at Log_Msg.cpp:2197</span><br style="color: #009902;">
<span style="color: #009902;">#6&nbsp; 0xb7ed749b in ACE_Log_Msg::log
(this=0x8588a58, format_str=0xb7f9ef3d "", log_priority=LM_TRACE,
argp=0xb7f9ef3c "") at Log_Msg.cpp:2047</span><br style="color: #009902;">
<span style="color: #009902;">#7&nbsp; 0xb7ed8055 in ACE_Log_Msg::log
(this=0x8588a58, log_priority=LM_TRACE, format_str=0xb7f9ef2a "%*s(%t)
leaving %s") at Log_Msg.cpp:961</span><br style="color: #009902;">
<span style="color: #009902;">#8&nbsp; 0xb7f9d9b3 in ~JAWS_Trace (this=0x2befe338) at Trace.cpp:139</span><br style="color: #009902;">
<span style="color: #009902;">#9&nbsp; 0xb70ff7b3 in StorageWriteState::service (this=0x85aaa28, ec=0x291b4d70, data=0x291b4d70) at StorageWriteState.cpp:63</span><br style="color: #009902;">
<span style="color: #009902;">#10 0xb7f94739 in JAWS_Protocol_Handler::service (this=0x291b4d70) at Protocol_Handler.cpp:38</span><br style="color: #009902;">
<span style="color: #009902;">#11 0xb7f86e0e in JAWS_Concurrency_Impl::svc (this=0x8057a34) at Concurrency.cpp:38</span><br style="color: #009902;">
<span style="color: #009902;">#12 0xb7f1c794 in ACE_Task_Base::svc_run (args=0x8057a34) at Task.cpp:258</span><br style="color: #009902;">
<span style="color: #009902;">#13 0xb7f1d108 in ACE_Thread_Adapter::invoke_i (this=0x8057ca8) at Thread_Adapter.cpp:151</span><br style="color: #009902;">
<span style="color: #009902;">#14 0xb7f1d2d6 in ACE_Thread_Adapter::invoke (this=0x8057ca8) at Thread_Adapter.cpp:95</span><br style="color: #009902;">
<span style="color: #009902;">#15 0xb7eb0c51 in ace_thread_adapter (args=0x8057ca8) at Base_Thread_Adapter.cpp:137</span><br style="color: #009902;">
<span style="color: #009902;">#16 0xb7e04240 in start_thread () from /lib/tls/i686/cmov/libpthread.so.0</span><br style="color: #009902;">
<span style="color: #009902;">#17 0xb7c7a4ae in clone () from /lib/tls/i686/cmov/libc.so.6</span><br>
<br>
&nbsp;&nbsp;&nbsp; 因为多次崩溃的调用栈都一样，这说明这个问题是一个比较确定的可再现bug，这为找到问题原因并最后解决提供了有利条件。<br>
&nbsp;&nbsp;&nbsp;
问题出现后，我们首先定位问题的原因与应用程序的日志输出有关系。经过代码复查，没有发现这方面的任何问题。接下来，我们只好怀疑ACE的日志相关类的源
码存在问题。我们又彻底阅读了与日志输出有关的类的源代码：ACE_Log_Msg，ACE_Log_Record，
ACE_Logging_Strategy，包括<span style="color: #000102;">ACE_Logging_Strategy::handle_timeout（）方法的实现代码，</span>重
点关注代码中关于内存操作和多线程安全互斥的操作，依然没有发现可疑的地方。这下问题就复杂了，总不能怀疑C++标准库的流类库有问题吧？这不太可能，但
是问题的直接爆发点是出在C++标准库里的，所以只能从标准库出发寻找线索。有过一定程序开发尤其是c/c++程序开发经验的人都知道，一些复杂问题直接
暴露出来的现象都是问题的表象，根本的原因可能跟表象差的很远，但是顺着表象提供的线索去寻找根源毕竟是解决问题的正道。于是我们开始从程序崩溃点寻找问
题的症结。<br>
&nbsp;&nbsp;&nbsp; 首先，我们分析程序为什么在调用memcpy（）这个标准c的库函数时发生崩溃的。没有源代码，怎么分析？学一点汇编语言的基础知识吧，这是对一个资深程序员的必要的技术要求。在gdb里使用如下命令：<br>
<span style="color: #009902;">(gdb) set disassembly-flavor intel</span><br style="color: #009902;">
<span style="color: #009902;">(gdb) disass</span><br>
gdb就给出了memcpy（）函数的汇编指令：<br>
<span style="color: #009902;">Dump of assembler code for function memcpy:</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb30 &lt;memcpy+0&gt;:&nbsp; mov&nbsp;&nbsp;&nbsp; ecx,DWORD PTR [esp+12]</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb34 &lt;memcpy+4&gt;:&nbsp; mov&nbsp;&nbsp;&nbsp; eax,edi</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb36 &lt;memcpy+6&gt;:&nbsp; mov&nbsp;&nbsp;&nbsp; edi,DWORD PTR [esp+4]</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb3a &lt;memcpy+10&gt;: mov&nbsp;&nbsp;&nbsp; edx,esi</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb3c &lt;memcpy+12&gt;: mov&nbsp;&nbsp;&nbsp; esi,DWORD PTR [esp+8]</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb40 &lt;memcpy+16&gt;: cld&nbsp;&nbsp;&nbsp;</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb41 &lt;memcpy+17&gt;: shr&nbsp;&nbsp;&nbsp; ecx,1</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb43 &lt;memcpy+19&gt;: jae&nbsp;&nbsp;&nbsp; 0xb7c1bb46 &lt;memcpy+22&gt;</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb45 &lt;memcpy+21&gt;: movs&nbsp;&nbsp; BYTE PTR es:[edi],BYTE PTR ds:[esi]</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb46 &lt;memcpy+22&gt;: shr&nbsp;&nbsp;&nbsp; ecx,1</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb48 &lt;memcpy+24&gt;: jae&nbsp;&nbsp;&nbsp; 0xb7c1bb4c &lt;memcpy+28&gt;</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb4a &lt;memcpy+26&gt;: <span style="color: #ff0102;">movs&nbsp;&nbsp; WORD PTR es:[edi],WORD PTR ds:[esi]</span></span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb4c &lt;memcpy+28&gt;: rep movs DWORD PTR es:[edi],DWORD PTR ds:[esi]</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb4e &lt;memcpy+30&gt;: mov&nbsp;&nbsp;&nbsp; edi,eax</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb50 &lt;memcpy+32&gt;: mov&nbsp;&nbsp;&nbsp; esi,edx</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb52 &lt;memcpy+34&gt;: mov&nbsp;&nbsp;&nbsp; eax,DWORD PTR [esp+4]</span><br style="color: #009902;">
<span style="color: #009902;">0xb7c1bb56 &lt;memcpy+38&gt;: ret</span><br>
再使用如下命令：<br>
<span style="color: #009902;">(gdb) p $eip</span><br>
gdb就打印出：<br>
<span style="color: #009902;">$1 = (void (*)(void)) 0xb7c1bb4a &lt;memcpy+26&gt;</span><br>
这就是gdb精确地告诉我们程序是在执行&#8220;movs WORD PTR es:[edi],WORD PTR
ds:[esi]&#8221;指令时发生了错误，操作系统产生了&#8220;signal 11, Segmentation
fault&#8221;信号，也就是说程序访问了非法的内存地址。我们观察movs指令，很有可能edi寄存器中的地址是非法的，于是在gdb中输入命令：<br>
<span style="color: #009902;">(gdb) p/x $edi</span><br>
gdb就打印出：<br>
<span style="color: #009902;">$2 = 0x0</span><br>
这就证明的确是因为edi寄存器被赋予了NULL指针导致问题的出现的。有经验的程序员立刻可以意识到进一步的问题症结是调用栈中的上层函数向
memcpy（）传递了错误的目标地址参数进而导致程序崩溃的。观察调用栈，位于memcpy（）之上的3层函数都是C++标准库的流操作。我想绝大多数
程序员都是只知道怎么正确使用标准流类库，而未研究过其是如何实现的吧？我们也是。但如果要解决问题，就必须弄清楚其内部实现。好在标准流类库都是模板实
现，有源码可读。不读不知道，标准流类库的实现代码还是很复杂的，晦涩难懂。可是我们也不是无目的的阅读全部代码，我们的近期目标是找到传递给
memcpy（）的指针是从哪来的，以及这个地址是如何被设成NULL的。我们找到最直接调用memcpy（）的C++标准库源码（看起来在这里我只写了
一句话，但是的确费了很多脑细胞才确定这里的源代码跟调用栈里显示的那些被调用的函数是同一个版本的，确定这一点很重要）：<br>
<span style="color: #009902;">template&lt;typename _CharT, typename _Traits&gt;</span><br style="color: #009902;">
<span style="color: #009902;">streamsize</span><br style="color: #009902;">
<span style="color: #009902;">basic_streambuf&lt;_CharT, _Traits&gt;::xsputn(const char_type* __s, streamsize __n)</span><br style="color: #009902;">
<span style="color: #009902;">{</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; streamsize __ret = 0;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (__ret &lt; __n)</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp; {</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp; const streamsize __buf_len = this-&gt;epptr() - this-&gt;pptr();</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp; if (__buf_len)</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const streamsize __remaining = __n - __ret;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const streamsize __len = std::min(__buf_len, __remaining);</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="color: #ff0102; font-weight: bold;">traits_type::copy(this-&gt;pptr(), __s, __len)</span><span style="color: #ff0102;">;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __ret += __len;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __s += __len;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this-&gt;pbump(__len);</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</span><br style="color: #009902;">
<br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp; if (__ret &lt; __n)</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; int_type __c = this-&gt;overflow(traits_type::to_int_type(*__s));</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!traits_type::eq_int_type(__c, traits_type::eof()))</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; ++__ret;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; ++__s;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; }</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return __ret;</span><br style="color: #009902;">
<span style="color: #009902;">}</span><br>
&nbsp;&nbsp; 上面红色显示的部分就是对memcpy（）函数的调用语句。根据前面的分析，我们知道出问题的时候&#8220;<span style="color: #000102;">this-&gt;pptr()&#8221;必然返回了NULL，所以memcpy（）执行时才会出错。</span><span style="color: #000102;">&#8220;this-&gt;pptr()&#8221;</span><span style="color: #000102;">是内联方法：<br>
<span style="color: #009902;">char_type* pptr() const { return _M_out_cur; }</span></span><br>
<span style="color: #000102;">这也就是说出问题的时候</span>basic_streambuf&lt;&gt;对象的<span style="color: #000102;">_M_out_cur成员被设成了NULL，所以才会引发连锁反应。似乎我们快要找到问题根源了，但是，问题没这么简单。因为我们分析了上面的</span>basic_streambuf&lt;&gt;::xsputn（）源码，发现只有__buf_len大于0的时候程序才可能会走到memcpy（）函数调用中，而在我们的程序出问题的时候，&#8220;this-&gt;epptr()&#8221;居然返回的也是NULL！<span style="color: #000102;">&#8220;this-&gt;epptr()&#8221;</span><span style="color: #000102;">也是内联方法：<br></span><span style="color: #000102;"><span style="color: #009902;">char_type* epptr() const { return _M_out_end; }</span></span><br>
<span style="color: #000102;">这也就是说出问题的时候</span>basic_streambuf&lt;&gt;对象的<span style="color: #000102;">_M_out_end成员也被设成了NULL。</span>这个时候计算得到的__buf_len值应该等于0，但程序怎么会进入到对memcpy（）函数的调用中呢？对此我们百思不得其解，但有一个信念：CPU保证会按程序的机器指令代码行事，不会乱来的<span style="color: #000102;">。于是</span>，<span style="color: #000102;">我们分析在我们的应用程序是多线程的情形下，合理解释只能是：当前线程执行到</span>__buf_len的计算的代码时返回的长度还是大于0的，于是会有后来的对<span style="color: #000102;">memcpy（）的调用；而同时可能有另外一个未做多线程同步的线程却修改了</span><span style="color: #000102;">&#8220;this-&gt;pptr()&#8221;及&#8220;</span>this-&gt;epptr()<span style="color: #000102;">&#8221;对应的指针值为NULL，这样当前线程执行到</span><span style="color: #000102;">memcpy（）时就会出错。分析到此，我们又有了新方向。下一步的目标就是</span><span style="color: #000102;">确定</span><span style="color: #000102;">哪个线程中的什么地方的代码有可能会将</span>basic_streambuf&lt;&gt;对象的<span style="color: #000102;">_M_out_cur成员及</span><span style="color: #000102;">_M_out_end成员</span><span style="color: #000102;">设成NULL。说起来容易做起来难啊！我们阅读了大量源代码，隐约发现有数个地方可能会修改</span><span style="color: #000102;">_M_out_cur成员值为NULL，但细细推敲起来都不应该导致出现上述问题。我们陷入僵局了。<br>
&nbsp;&nbsp;&nbsp;&nbsp; 所以我这里再次强调，一个资深的c/c++程序员应该懂得一些汇编语言的基础知识并掌握至少一个汇编调试器的使用技巧。在这里，gdb就够用了。因为gdb支持&#8220;内存读写断点&#8221;的设置。内存读写断点是CPU为调试软件设置的专用中断器</span><span style="color: #000102;">，它能够监视指定的内存段范围内是否有内存读</span><span style="color: #000102;">，</span><span style="color: #000102;">或内存写</span><span style="color: #000102;">，</span><span style="color: #000102;">或内存读写操作</span><span style="color: #000102;">，</span><span style="color: #000102;">如果有则产生中断</span><span style="color: #000102;">，</span><span style="color: #000102;">并由调试器接管程序的执行</span><span style="color: #000102;">。在gdb里就可以设置这样的断点来监视</span>basic_streambuf&lt;&gt;对象的<span style="color: #000102;">_M_out_cur成员的值何时被修改</span><span style="color: #000102;">，</span><span style="color: #000102;">从而使我们知道在哪个线程执行到哪个函数时修改了</span><span style="color: #000102;">_M_out_cur成员的值</span><span style="color: #000102;">，以便我们分析导致程序崩溃问题的根源</span><span style="color: #000102;">。但困难在于</span><span style="color: #000102;">，</span><span style="color: #000102;">设置这样的断点时需要指定一个内存地址</span><span style="color: #000102;">，</span><span style="color: #000102;">也就是我们要监视的</span>basic_streambuf&lt;&gt;对象的<span style="color: #000102;">_M_out_cur成员的内存地址</span><span style="color: #000102;">，</span><span style="color: #000102;">这个如何确定呢?<br>
&nbsp;&nbsp;&nbsp;&nbsp; 还是汇编！在我们的应用程序中，寻找</span><span style="color: #000102;">_M_out_cur成员</span><span style="color: #000102;">的内存地址</span><span style="color: #000102;">的思路是这样的：我们观察前面给出的调用栈</span><span style="color: #000102;">，</span><span style="color: #000102;">可以看到在第4层函数</span>ACE_Log_Record::print()中的最后一个参数的是"s=@0x80521c8"，结合ACE源码知道参数s的类型是ostream&lt;&gt; &amp;，这其实就是告诉我们一个ostream&lt;&gt;对象的地址在0x80521c8。<span style="color: #000102;">而实际上这个</span>ostream&lt;&gt;对象的子类型就是ofstream&lt;&gt;，也就是说在我们的应用程序中多个线程操作着地址在0x80521c8的同一个ofstream&lt;&gt;对象进行日志输出操作<span style="color: #000102;">。阅读</span>C+
+标准库源码我们知道，如果知道了一个ofstream&lt;&gt;对象的首地址，就可以计算其_M_filebuf对象成员的首地址，该成员的类型
为basic_filebuf&lt;&gt;；知道了一个basic_filebuf&lt;&gt;对象的首地址，就可以知道其继承自基类
basic_streambuf&lt;&gt;的<span style="color: #000102;">M_out_cur成员的地址了。这些地址的计算都是根据一个原理：对象的每个成员的首地址相对于该对象的首地址的偏移量是</span><span style="color: #000102;">一个在</span><span style="color: #000102;">编译期就确定的常量，对象的首地址加上这个特定的偏移量而得到的值就是对应成员的首地址。那么这些偏移量具体数值如何知道？汇编吧！我们写了一个简单的测试程序专门用于获取我们所关心的两个偏移量的数值。测试程序是这样的：<br>
<span style="color: #009902;">#include &lt;iostream&gt;</span><br style="color: #009902;">
<span style="color: #009902;">#include &lt;fstream&gt;</span><br style="color: #009902;">
<span style="color: #009902;">using namespace std;</span><br style="color: #009902;">
<span style="color: #009902;">void work(ostream &amp; s)</span><br style="color: #009902;">
<span style="color: #009902;">{</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"pos="&lt;&lt;s.tellp()&lt;&lt;endl;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s&lt;&lt;"hello,world!"&lt;&lt;endl;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"pos="&lt;&lt;s.tellp()&lt;&lt;endl;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;</span><br style="color: #009902;">
<span style="color: #009902;">}</span><br style="color: #009902;">
<br style="color: #009902;">
<span style="color: #009902;">int main()</span><br style="color: #009902;">
<span style="color: #009902;">{</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ofstream out;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.open("123.txt");</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( !out.is_open() )</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"open file failed!"&lt;&lt;endl;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="color: #009902;">
<br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; work(out);</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;</span><br style="color: #009902;">
<span style="color: #009902;">}</span><br></span>&nbsp;&nbsp;&nbsp;
经过一翻外科手术式的剖析，我们找到了这两个偏移量的数值（虽然这里只写了一句话，但这绝不仅仅是一句话），并且弄清楚了一个ofstream&lt;
&gt;对象的各个成员在内存中的布局（仅在g++
4.1.2编译的目标代码中进行了验证）。如果一个ofstream&lt;&gt;对象的首地址在0x8049240，则此对象的内存布局为：<br>
<span style="color: #009902;">address&nbsp;&nbsp;</span> <span style="color: #009902;">:</span><span style="color: #009902;">&nbsp;&nbsp; hex value dump</span><br>
<span style="color: #009902;">0x8049240 :&nbsp;&nbsp;</span> <span style="color: #0001ff; font-weight: bold;">0xb7f6a9cc</span><span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="color: #ff01ff; font-weight: bold;">0xb7f6aac8</span><span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x0804a170&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x0804a170</span><br>
<span style="color: #009902;">0x8049250 :&nbsp;&nbsp; 0x0804a170&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="color: #ff0102; font-weight: bold;">0x00000000</span><span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br>
<span style="color: #009902;">0x8049260 :&nbsp;&nbsp; 0xb7f6dbbc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x8049270 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x0804a008</span><br style="color: #009902;">
<span style="color: #009902;">0x8049280 :&nbsp;&nbsp; 0x00000001&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #0099cc; font-weight: bold;">0x00000030</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x8049290 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br>
<span style="color: #009902;">0x80492a0 :&nbsp;&nbsp;</span> <span style="color: #660102; font-weight: bold;">0x0804a170</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff9933; font-weight: bold;">0x00002000</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #009902;">0x00000001&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br>
<span style="color: #009902;">0x80492b0 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xb7f6df88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x80492c0 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xb7f6a9e0</span><br style="color: #009902;">
<span style="color: #009902;">0x80492d0 :&nbsp;&nbsp; 0x00000006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00001002&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x80492e0 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x80492f0 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x8049300 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x8049310 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x8049320 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br style="color: #009902;">
<span style="color: #009902;">0x8049330 :&nbsp;&nbsp; 0x00000008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x080492f0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xb7f6dbbc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000</span><br>
<span style="color: #009902;">0x8049340 :&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x08049244&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xb7f6dd40&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xb7f6df80</span><br style="color: #009902;">
<span style="color: #009902;">0x8049350 :&nbsp;&nbsp; 0xb7f6df78&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000000<br></span><span style="color: #009902;">......</span><span style="color: #009902;">......</span><span style="color: #009902;">......</span><br>
在上面，我们知道<span style="color: #ff01ff;"><span style="color: #000102;">从</span></span><span style="color: #ff01ff; font-weight: bold;">0xb7f6aac8</span>开始的内存就是ofstream&lt;&gt;对象所聚合的basic_filebuf&lt;&gt;对象成员_M_filebuf的内存映像；而<span style="color: #ff0102; font-weight: bold;">0x00000000</span>就是该_M_filebuf对象的<span style="color: #000102;">M_out_cur成员的内存映像，也就是说我们可以直接根据</span>ofstream&lt;&gt;对象的首地址然后加上偏移量0x18而得到<span style="color: #000102;">M_out_cur成员的内存地址</span><span style="color: #000102;">。关于</span>ofstream&lt;&gt;对象及basic_filebuf&lt;&gt;对象在内存中的布局我们还研究了更多的东西。例如，<span style="color: #0001ff; font-weight: bold;">0xb7f6a9cc</span><span style="color: #000102;">所</span>指向的地址就是ofstream&lt;&gt;类的虚拟函数表的首地址；<span style="color: #ff01ff; font-weight: bold;">0xb7f6aac8</span><span style="color: #000102;">所</span>指向的地址就是basic_filebuf&lt;&gt;类的虚拟函数表的首地址；<span style="color: #009902;"><span style="color: #0099cc; font-weight: bold;">0x00000030</span></span><span style="color: #000102;">就是</span>basic_filebuf&lt;&gt;类的_M_mode成员的值，其值等价于(ios_base::out | ios_base::trunc)；<span style="color: #660102; font-weight: bold;">0x0804a170</span><span style="color: #000102;">所</span>指向的地址就是basic_filebuf&lt;&gt;对象内部的一个真正内部缓冲区的首地址，对应于该对象的_M_buf成员；<span style="color: #ff9933; font-weight: bold;">0x00002000</span><span style="color: #000102;">则指出这个缓冲区的长度是8192字节，对应于该对象的_M_buf_size成员。<br>
&nbsp;&nbsp;&nbsp;&nbsp; 有了上面得到的内存地址计算方法，下一步就是在我们的应用程序中获得</span>ofstream&lt;
&gt;对象的首地址了。这有很多方法，我们可以直接在gdb中找到ACE_Log_Msg::log（）方法的源码并设置断点，然后启动程序，待断点激
活后，在gdb中打印调用栈而取得ofstream&lt;&gt;对象的首地址。这里我们使用了另外一种方法：我们根据前面给出的调用栈中的第1层的函
数返回地址<span style="color: #000102;">0xb7d9c006</span>，在gdb中进行反汇编：<br>
<span style="color: #009902;">(gdb) disass 0xb7d9c006</span><br style="color: #009902;">
<span style="color: #009902;">Dump of assembler code for function <span style="color: #ff0102;">_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci</span>:</span><br style="color: #009902;">
<span style="color: #009902;">0xb7d9bfa0 &lt;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci+0&gt;: push&nbsp;&nbsp; ebp</span><br style="color: #009902;">
<span style="color: #009902;">0xb7d9bfa1 &lt;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci+1&gt;: mov&nbsp;&nbsp;&nbsp; ebp,esp</span><br style="color: #009902;">
<span style="color: #009902;">0xb7d9bfa3 &lt;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci+3&gt;: push&nbsp;&nbsp; edi</span><br style="color: #009902;">
<span style="color: #009902;">0xb7d9bfa4 &lt;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci+4&gt;: push&nbsp;&nbsp; esi</span><br style="color: #009902;">
<span style="color: #009902;">0xb7d9bfa5 &lt;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci+5&gt;: push&nbsp;&nbsp; ebx</span><br style="color: #009902;">
<span style="color: #009902;">0xb7d9bfa6 &lt;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci+6&gt;: sub&nbsp;&nbsp;&nbsp; esp,0x2c</span><br style="color: #009902;">
<span style="color: #009902;">0xb7d9bfa9 &lt;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci+9&gt;: mov&nbsp;&nbsp;&nbsp; eax,DWORD PTR [ebp+16]</span><br style="color: #009902;">
<span style="color: #009902;">......</span><br>
我们可以看到，gdb打印出了调用栈中第1层的函数的C++名称经过编码后所对应的C名称字符串为&#8220;_ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci&#8221;，这个C名称与其对应的C++名称&#8220;<span style="color: #000102;">std::basic_streambuf&lt;char, std::char_traits&lt;char&gt; &gt;::xsputn</span>&#8221;是等价的。接下来，我们启动一个新的gdb程序，并在gdb中加载应用程序文件，在启动应用程序运行之前，先设置断点：<br>
<span style="color: #009902;">(gdb) break _ZNSt15basic_streambufIcSt11char_traitsIcEE6xsputnEPKci</span><br>
这就是要在前述的调用栈的第1层函数入口处设置断点。然后我们启动应用程序的执行并激活日志输出操作，当第一条日志输出时，刚才所设断点就激活了，gdb
将中断程序的执行，此时我们打印调用栈，gdb将输出与前述类似的调用栈，此时我们可以轻松地从调用栈中取得ofstream&lt;&gt;对象的首地
址。我们将这个地址加上偏移量0x18，就得到了我们要监视的内存地址。在我们的应用程序中计算得到的地址是0x80521e0。<br>
&nbsp;&nbsp;&nbsp; 有了要监视的内存地址以后，我们就可以在gdb里设置&#8220;内存写&#8221;断点了，输入如下命令：<br>
<span style="color: #009902;">(gdb) watch *(int *)0x80521e0</span><br style="color: #009902;">
<span style="color: #009902;">Hardware watchpoint 1: *(int *) 134554080</span><br>
这表明一个&#8220;内存写&#8221;断点设置成功。接下来让gdb执行c命令继续执行应用程序，就可以开始监视basic_filebuf&lt;&gt;对象的<span style="color: #000102;">M_out_cur成员何时被修改成NULL了。当断点激活时，gdb打印：</span><br>
<span style="color: #000102;"><span style="color: #009902;">Hardware watchpoint 3: *(int *) 134554080</span><br style="color: #009902;">
<br style="color: #009902;">
<span style="color: #009902;">Old value = 134555784</span><br style="color: #009902;">
<span style="color: #009902;">New value = 0</span><br style="color: #009902;">
<span style="color: #009902;">0xb7ce3088 in std::basic_filebuf&lt;char, std::char_traits&lt;char&gt; &gt;::_M_seek () from /usr/lib/libstdc++.so.6</span><br>
此时打印调用栈，gdb显示：<br>
<span style="color: #009902;">(gdb) bt</span><br style="color: #009902;">
<span style="color: #009902;">#0&nbsp; 0xb7ce3088 in std::basic_filebuf&lt;char, std::char_traits&lt;char&gt; &gt;::_M_seek () from /usr/lib/libstdc++.so.6</span><br style="color: #009902;">
<span style="color: #009902;">#1&nbsp; 0xb7ce4e2a in std::basic_filebuf&lt;char, std::char_traits&lt;char&gt; &gt;::seekoff () from /usr/lib/libstdc++.so.6</span><br style="color: #009902;">
<span style="color: #009902;">#2&nbsp; 0xb7d0be5d in <span style="color: #ff0102;">std::ostream::tellp ()</span> from /usr/lib/libstdc++.so.6</span><br style="color: #009902;">
<span style="color: #009902;">#3&nbsp; 0xb7e560a4 in ACE_Logging_Strategy::handle_timeout (this=0xb7f24ac0) at Logging_Strategy.cpp:404</span><br style="color: #009902;">
<span style="color: #009902;">#4&nbsp; 0xb7e24cb1 in ACE_Event_Handler_Handle_Timeout_Upcall&lt;ACE_Recursive_Thread_Mutex&gt;::timeout (this=0x80515d0,</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; timer_queue=@0x8051528, event_handler=0xb7f24ac0, act=0x0, recurring_timer=1, cur_time=@0x2ccff2e8)</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; at /data/jinwei/svn_root/ireport/common/ace/ACE_wrappers/ace/Timer_Queue_T.cpp:408</span><br style="color: #009902;">
<span style="color: #009902;">#5&nbsp; 0xb7e4220d in ACE_Dev_Poll_Reactor::dispatch_timer_handler (this=0x8051470, guard=@0x2ccff394)</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; at /data/jinwei/svn_root/ireport/common/ace/ACE_wrappers/ace/Timer_Queue_T.inl:170</span><br style="color: #009902;">
<span style="color: #009902;">#6&nbsp; 0xb7e4492c in ACE_Dev_Poll_Reactor::dispatch (this=0x8051470, guard=@0x2ccff394) at Dev_Poll_Reactor.cpp:1230</span><br style="color: #009902;">
<span style="color: #009902;">#7&nbsp; 0xb7e44a56 in ACE_Dev_Poll_Reactor::handle_events_i (this=0x8051470, max_wait_time=0x0, guard=@0x2ccff394)</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; at Dev_Poll_Reactor.cpp:1211</span><br style="color: #009902;">
<span style="color: #009902;">#8&nbsp; 0xb7e44b21 in ACE_Dev_Poll_Reactor::handle_events (this=0x8051470, max_wait_time=0x0) at Dev_Poll_Reactor.cpp:1166</span><br style="color: #009902;">
<span style="color: #009902;">#9&nbsp; 0xb7e82f02 in ACE_Reactor::run_reactor_event_loop (this=0x80518f0, eh=0) at Reactor.cpp:233</span><br style="color: #009902;">
<span style="color: #009902;">#10 0xb7f0dbe7 in JAWS_Event_Dispatcher::JAWS_Event_Dispatcher_Reactor_Event_Loop () at Event_Dispatcher.cpp:32</span><br style="color: #009902;">
<span style="color: #009902;">#11 0xb7e99108 in ACE_Thread_Adapter::invoke_i (this=0x85b3918) at Thread_Adapter.cpp:151</span><br style="color: #009902;">
<span style="color: #009902;">#12 0xb7e992d6 in ACE_Thread_Adapter::invoke (this=0x85b3918) at Thread_Adapter.cpp:95</span><br style="color: #009902;">
<span style="color: #009902;">#13 0xb7e2cc51 in ace_thread_adapter (args=0x85b3918) at Base_Thread_Adapter.cpp:137</span><br style="color: #009902;">
<span style="color: #009902;">#14 0xb7d80240 in start_thread () from /lib/tls/i686/cmov/libpthread.so.0</span><br style="color: #009902;">
<span style="color: #009902;">#15 0xb7bf64ae in clone () from /lib/tls/i686/cmov/libc.so.6</span><br>
此时，我们</span><span style="color: #000102;">终于有了重大发现</span><span style="color: #000102;">！</span><span style="color: #000102;">原来ACE_Logging_Strategy::handle_timeout（）方法中执行了如下代码：<br>
<span style="color: #009902;">int</span><br style="color: #009902;">
<span style="color: #009902;">ACE_Logging_Strategy::handle_timeout (const ACE_Time_Value &amp;,</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const void *)</span><br style="color: #009902;">
<span style="color: #009902;">{</span><br style="color: #009902;">
<span style="color: #009902;">#if defined (ACE_LACKS_IOSTREAM_TOTALLY)</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp; if ((size_t) ACE_OS::ftell (this-&gt;log_msg_-&gt;msg_ostream ()) &gt; this-&gt;max_size_)</span><br style="color: #009902;">
<span style="color: #009902;">#else</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp; if ((size_t)</span> <span style="color: #ff0102; font-weight: bold;">this-&gt;log_msg_-&gt;msg_ostream ()-&gt;tellp ()</span> <span style="color: #009902;">&gt; this-&gt;max_size_)</span><br style="color: #009902;">
<span style="color: #009902;">#endif /* ACE_LACKS_IOSTREAM_TOTALLY */</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp; {</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Lock out any other logging.</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this-&gt;log_msg_-&gt;acquire ())</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACE_ERROR_RETURN ((LM_ERROR,</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACE_LIB_TEXT ("Cannot acquire lock!")),</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -1);</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .....</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="color: #009902;">
<span style="color: #009902;">&nbsp;&nbsp;&nbsp;&nbsp; .....</span><br style="color: #009902;">
<span style="color: #009902;">}</span><br></span>可以看到，对ostream&lt;&gt;::tellp()方法的调用居然会导致ofstream&lt;&gt;对象内部的basic_streambuf&lt;&gt;对象成员的<span style="color: #000102;">M_out_cur成员的值</span>被修改<span style="color: #000102;">为NULL！而</span><span style="color: #000102;">ACE_Logging_Strategy::handle_timeout（）方法在调用</span>ostream&lt;&gt;::tellp()方法时居然没有先获得锁！知道这个根本原因了，问题就好解决了，至于如何修改<span style="color: #000102;">ACE_Logging_Strategy::handle_timeout（）的代码，明眼人一看便知了。我们修改代码后并进行了长期的压力测试的验证，最后确认了这就是问题的根源。<br></span>&nbsp;&nbsp;&nbsp; 这个问题被解决后，我们查看了ACE最新版本5.6.2中的代码，发现源码中仍然存在此问题。于是我们给ACE开发社区发了一个帖子：<a  href="http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/63d6fe4a3753ba45?hl=zh-CN#b286920e87793ed2" target="_blank">A bug about ACE_Logging_Strategy</a> 来报告这个ACE中的bug，算是为ACE开发社区做个贡献吧。<br>
细细推敲起来，开发ACE的大牛们为什么会犯这种有点低级的错误呢？而且这么长时间都没有发现这个问题？也许他们没有进行过像我们这么大压力的长期测
试吧？也许他们也和我们当初所认为的一样，以为ostream&lt;&gt;::tellp()即便不是一个const方法，也应该是个&#8220;准&#8221;
const方法吧？然而事实却并非如此。其实从C++程序员的角度来说，我们的确需要一个const版本的ostream&lt;&gt;::tellp
()接口，不知道将来C++标准会不会在这个问题上有所改变。对于这个诉求，不只我们有，这里也有: <a  href="http://www.thescripts.com/forum/thread139342.html" target="_blank">why is ostream::tellp not const</a>。<img src ="http://www.cppblog.com/giftedchen/aggbug/58122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/giftedchen/" target="_blank">搞搞陈</a> 2008-08-06 11:37 <a href="http://www.cppblog.com/giftedchen/archive/2008/08/06/58122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>