青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

asm, c, c++ are my all
-- Core In Computer
posts - 139,  comments - 123,  trackbacks - 0

[轉]Track'em Down...

http://m.shnenglu.com/jerysun0818/archive/2006/06/04/8153.html



P.S. 很多朋友都抱怨說STL出問題的時候debug很難,編譯期錯誤算是輕的,大不了一串串令人頭暈的出錯信息,至少還能雙擊定位到錯誤行。而神秘的運行期崩潰才是真正令人頭大的問題。下面就是一個比較典型的、五臟俱全的運行期崩潰事件,從幾行簡簡單單的代碼,似乎根本不可能崩潰,一直到最后揪出隱藏在背后的機制。其中的思維分析過程是怎樣的呢?希望對一些朋友有點幫助。標題起作”Track’em Down”一方面是暗指整個分析跟蹤的過程,而是最后所揭露出的機制的確是個tracking機制:-)

很久沒寫blog了,一來是諸事纏身,二來也是實在不像以前那么有熱情坐下來好好寫篇技術文章了。很多時候只是做個旁觀者,四處潛水而已。昨天又跟老婆鬧了矛盾,心里郁悶,于是給自己一個理由四處亂逛,跑到許久未去的cpper( http://www.cpper.com/c/ )上,cpper還是一如既往的冷清,看來一個論壇要想火起來光靠技術是不行的,cpper(前身為allaboutprogram)圈子里有一批技術很牛的朋友,也許是太牛了,這兩年都開始一個個牛得沒影兒了。二來cpper不像CSDN這樣有一個門戶,主頁上有亂七八糟的好玩信息,新聞,八卦,等等不一而足。國內C++社群的大部分朋友想必是不知道cpper的,尤其是初學者。實在是個不小的遺憾…等等…似乎扯遠了,剛才說到逛到cpper上,看到tomato的一個帖子,提到一段行為怪異的代碼,行為是崩潰,而且是在l析構的時候崩潰:

#include <list> ????????
??????????
int main(int argc, char* argv[])
{

? ? std::list<int> l;

? ? std::list<int>::iterator* it1 = new std::list<int>::iterator(l.begin());

? ? memset(it1, 1, sizeof(std::list<int>::iterator));

? ? l.push_back(1);

? ? l.begin();

? ? return0;

以上是原貼里的代碼,我就不簡化了。大伙看著辦吧,呵呵。

乍看上去這段代碼似乎不應該有什么問題(至少不會崩潰)。最可疑的當然是那行memset,但memset只是把it1指向的一個new出來的iterator給corrupt掉了,況且這個it1也沒被delete,所以也就不會因析構而崩潰了(雖然有內存泄漏,但tomato說的是崩潰),另外it1跟l看上去是井水不犯河水。所以怎么看不像有問題的樣子。

以上是眼睜睜瞪著代碼看出來的結果,然而當我把代碼塞到IDE里面,編譯運行之后卻發現果然如tomato所說,在list析構時程序崩潰了(很湊巧我跟tomato用的IDE都是VC8,這是一個關鍵條件)。對于這類問題,一般我是先黑箱再白箱。這么短的程序,要出問題肯定出在memset那行,邏輯上可以大致這么說:“由于memset把it1的內存corrupt掉了,結果導致l在析構的時候崩潰。”聽到自己下這么個結論我都覺得有點哭笑不得,這壓根兒風馬牛不相及嘛,it1被corrupt與l析構崩潰有屁關系?!it1只不過是個指向l的迭代器,說穿了就是裹著個指向l中的某個node的指針而已,它的死活怎么會影響到l的析構呢?

但福爾摩斯大致說過,去掉那些絕對不可能的,剩下的就是可能。這里這是唯一的可能,只不過問題是,這個邏輯還不夠細致,需要豐滿起來。進一步,要想使得l在析構的時候崩潰,這個memset肯定以某種方式影響到了l。又因為memset直接影響的是it1,那么必然是it1和l的某種聯系導致memset間接對l產生了影響。于是再來看,it1和l有什么聯系呢?得!這下看出來了,it1在構造的時候是通過l.begin()拷貝構造的,哈!問題似乎有點眉目了,但到此我還是想不通,根據常識,iterator的拷貝構造會產生出一個與源iterator不相干的副本,對這個副本干什么事怎么會影響到源呢?更別提影響到源iterator所指向的容器了。開玩笑!甭管他,既然已經肯定下來這是唯一的可能,那么推論只能是:背后還隱藏著什么不為人之的東西。于是進一步注釋掉“(l.begin())”,使it1變成缺省構造的迭代器,果然,不再崩潰了。于是,現在可以肯定作出的結論是:“… it1 = new std::list<int>::iterator(l.begin());這行代碼使得it1所指向的迭代器與l發生了某些關系(當然,不是it1指向l這樣的白癡關系)。很顯然,這個關系的建立點不可能是在l.begin(),因為這里還沒有涉及到it1,所以唯一的可能就是,這個關系是在std::list<int>::iterator的拷貝構造函數階段發生的。這次拷貝構造以l.begin()為參數,而l.begin()推測起來應當帶了l的信息,因而it1就利用這個機會與l建立起了“某種關系”。OK,到目前為止這個“某種關系”只是一個模糊的概念,我們并不明確知道究竟是什么關系,竟然導致list析構會失敗。看來是時候進入白箱了,F5,break,看斷在什么地方了,說實話P.J Plauger寫的STL代碼至少在外觀上不像SGI的那么親近人,nevermind,我看到了我想看到的信息——一個名為_HAS_ITERATOR_DEBUGGING的宏,哈,想起來了,這一版的STL是帶有iterator debugging功能的,所以當然能夠發現iterator被corrupt掉的情況。疑問來了,怎么發現的呢?直接的答案是,肯定有某種追蹤機制,而且根據前面的推理,由于是l在析構的時候發生的崩潰(it1并不析構,因為并沒有對它進行delete),所以l在析構的時候必然能以某種方式訪問到it1并發現它被corrupt掉的事實,再推廣之,l肯定能夠訪問到所有指向它的迭代器(這才叫iterator debugging嘛),而it1在構建的時候的確是拷貝構造l.begin()來的,也就是說指向l,所以l必然應當知道(跟蹤)它(it1)的存在,又因為接下來的memset是個相當low level/native的操作,所以l對此肯定不知情,還以為it1一直好端端的指著它的begin()處呢,最后當它一個個察訪指向它內部的迭代器,當查訪到it1時就發現it1被corrupt掉了,于是崩潰。呼~一切到這里似乎都串起來了。剩下的就是去發現list<int>::iterator的拷貝構造函數究竟在背后搗了什么鬼才使得l最終能夠訪問到it1的,在開始之前我就設想一個場景:肯定是l里面有一個鏈表,把所有指向它內部的迭代器串起來了,這樣它就能夠逐一檢查這些迭代器,看看比如說它們是否越界之類的。而it1在拷貝構造的時候由于是以l.begin()為參數的,l.begin()肯定帶有l的信息,說白了,指向l的指針,或l的引用,于是it1的拷貝構造函數就可以把自身鏈入l內部的鏈表中去。想到這里似乎情況十分明朗了,剩下的就是跟蹤進去驗證一下了…但是等一下…剛才說到,程序的現象是…崩潰!如果是在l對它的迭代器check的時候發現錯誤,大腦沒毛病的庫設計者肯定會拋出一個異常吧,不會是崩潰的癥狀…what the hell。反正我已經知道問題的核心在list<int>::iterator的拷貝構造函數那里,那是唯一同時擁有it1與l信息的地方。于是F11到list<int>::iterator的拷貝構造處,即“… it1 = new std::list<int>::iterator(l.begin());”這行代碼處,郁悶的是,調試器在這里一跳而過,似乎它擁有的是一個trivial的拷貝構造函數(從后來的結果來看這似乎是VC2005的一個小問題),而且我”go to disassembly”居然也就看到聊聊幾行代碼,除了一個對list<int>::begin()的調用之外就沒有其它調用了,傻眼了,似乎真的是個trivial的copy ctor?如果真是這樣的話,就不可能在這里建立起it1跟l之間的聯系了,因為trivial的copy constructing只會是把成員按位復制一下,沒有其它代碼,不涉及函數調用,怎么可能會有機會干其它事情呢?而后面又沒有任何地方同時涉及it1跟l的,那又怎么會最終導致l析構崩潰呢?簡單的推理,反推上去,既然l最終析構崩潰了,那么這個邏輯的某一環肯定錯了,最可能的就是,這并非trivial copy ctor,調試器欺騙了我的眼睛,背后肯定調用了某個拷貝構造函數,然后干了些勾當。于是靜態跟蹤派上了用場,幸好VS2005的intelli-sense非常強大,兩次“go to definition”就置身于了list<int>::iterator的定義里面,剛才不是說關鍵就在list<int>::iterator的拷貝構造函數里面嗎?于是瀏覽一下list<int>::iterator的定義,VC帶的STL的代碼真難看啊,好在這個類比較短,很遺憾,沒有拷貝構造函數,只有構造函數,不過碰巧,在其中一個由_HAS_ITERATOR_DEBUGGING條件控制的構造函數里面發現了一行寶貴的代碼:this->_Adopt(_Plist);,如果沒猜錯,這肯定是把這個迭代器自身鏈到它所指向的容器內的跟蹤鏈表中去的。_Plist(該構造函數的第二個參數)是什么?跟蹤到l.begin()調用里面就會發現,喂給list<int>::iterator的構造函數的第二個參數(也就是_Plist)是this,呵呵,看來this->_Adopt(_Plist)實際上就是把this(迭代器)“收養(adopt)”給_Plist啊。沒猜錯,果然是這樣的跟蹤機制。那么,照理說list<int>::iterator的拷貝構造函數也應該有相應的動作才對啊,不然拷貝構造出的新的iterator就會沒人“收養”了(而我們的跟蹤機制是應當跟蹤到每個“在世”的iterator的,否則就沒意義了)。剛才提到,list<int>::iterator本身沒有拷貝構造函數,那么只有一種可能,要么其成員具有non-trivial的拷貝構造函數,要么在基類里面。實際上list<int>::iterator只有一個成員,一如我們意料之中的,指向list的node的ptr。所以秘密肯定在基類中,順著基類一路找下去,_Const_iterator->_Bidit->_Iterator_base。說實話到_Bidit差點放棄,因為我以為_Bidit里面肯定就是一些typedef,就像unary_function那樣。事實卻不是這樣,下面還隱藏了一層_Iterator_base,而這個_Iterator_base就是一切秘密所在了。它是有拷貝構造函數的,代碼我就不列了,如果你跟蹤到這里,真相也就大白了,簡單的來說,它根據源iterator找到其所指的容器,然后取出該容器里面的用于跟蹤迭代器的鏈表頭指針,然后把自身(this)鏈到這個鏈表里面去。由于一個iterator誕生的方式一共就兩種,一種是從某個容器誕生,這時是調用的它的一般構造函數,容器會把自身的指針當作參數傳遞給這個iterator,后者通過這個容器指針來將其自身鏈接到容器的跟蹤鏈表中。第二個誕生方式就是這里說的,拷貝構造,拷貝構造時會將其自身鏈到源iterator所指向的容器內的跟蹤鏈表中去。反正就是,一切現有的iterator都會恰當被它所指的容器跟蹤著。

那么是時候揭穿謎底了吧,為什么memset會闖下這么大的禍?因為一個iterator要被鏈到鏈表里面,它肯定有next指針,這樣才能鏈成一個鏈表嘛。而memset粗暴地將這個next指針給重置了(事實上它把it1指向的迭代器整個給memset了,當然包括里面的next指針),這里,next指針被重置為了1(如果memset成0就不會崩潰了,原因很簡單,想想看),顯然,這就指向了一塊無意義的內存。于是,l在析構的時候,試圖遍歷并check它所跟蹤的iterator鏈表,隨著它通過next指針一節節跳轉,當到了我們的it1的時候,由于其next指針的值是1,所以試圖再跳(next)的時候就非法內存訪問了!崩潰!所以,這里的問題并不在于最后的check失敗了,而是在于迭代器鏈表被corrupt掉了,才導致的崩潰。這就解除了前面的關于未發生異常的疑惑。事情到此就大致結束了。

當然,這里還隱藏了很多的細節。例如最后l析構時并不是要去“check”每個迭代器。另外這個iterator tracking的機制還是很有代表性的,boost::signal里面就用了如出一轍的手法,這個手法充分顯示出了C++的強大和靈活。原來看上去毫不起眼的構造函數在背后還能做那么多工作。這里就不方方面面總結這一技術了,一是困了,二是這不是這篇blog的初衷,寫這篇blog一是為了平靜一下郁悶的心情,二是為了顯示一個從現象開始分析推理問題,最終接近答案的過程。程序員的大部分時間是在debug,這篇blog其實介紹的就相當于一個debug的思維過程,或許它并不是最好的,但希望你能夠在其中發現一些有用的東西。

P.S. 看上去啰啰嗦嗦一大通,實際上在腦子里轉來轉去是一瞬間的工夫,加上跟蹤(靜態/動態)也就十來分鐘,而寫這篇blog倒花了我四十多分鐘(這也是越來越不寫blog的原因吧),人類自然語言的表達力某些時候是十分啰唆冗余的…

posted on 2006-06-04 19:52 Jerry Cat 閱讀(331) 評論(0)  編輯 收藏 引用

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理



<2006年6月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

常用鏈接

留言簿(7)

隨筆檔案

最新隨筆

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            美脚丝袜一区二区三区在线观看| 在线观看欧美精品| 亚洲天堂网站在线观看视频| 亚洲国产精选| 久久精品99国产精品酒店日本| 亚洲午夜高清视频| 一区二区三区四区五区在线 | 国产精品视频免费| 国产精品视频免费在线观看| 国产精品啊啊啊| 国产精品日韩欧美大师| 国产亚洲精品一区二区| 精品成人国产在线观看男人呻吟| 狠久久av成人天堂| 亚洲国产一区在线观看| 99这里只有精品| 午夜精品亚洲| 噜噜噜躁狠狠躁狠狠精品视频| 欧美成人免费在线| 99视频精品全国免费| 午夜精品福利一区二区三区av| 久久久国产精品一区二区中文 | 国产精品久久久久久久免费软件| 国产情侣一区| 亚洲欧洲日夜超级视频| 亚洲性线免费观看视频成熟| 久久激情视频久久| 欧美激情精品久久久久久变态| 夜夜爽99久久国产综合精品女不卡| 午夜一区二区三视频在线观看 | 亚洲高清视频的网址| aa级大片欧美三级| 欧美资源在线| 国产精品xxxav免费视频| 一区二区在线观看视频| 亚洲一区免费视频| 欧美福利电影网| 亚洲欧美卡通另类91av| 欧美精品激情在线观看| 一区二区三区精品久久久| 午夜电影亚洲| 欧美人与性动交cc0o| 在线观看亚洲| 久久精品国产免费观看| 99re6热在线精品视频播放速度| 先锋影音国产精品| 国产精品爱啪在线线免费观看| 亚洲精品美女免费| 老牛影视一区二区三区| 亚洲欧美中文日韩v在线观看| 欧美日本韩国一区| 亚洲美女毛片| 欧美福利电影网| 久久青草久久| 黄色亚洲免费| 久久精品视频亚洲| 午夜精品久久久久久久99热浪潮| 欧美精品久久久久久| 亚洲精品日韩欧美| 亚洲第一狼人社区| 久久偷看各类wc女厕嘘嘘偷窃| 国内精品久久久久久 | 亚洲福利国产| 久久米奇亚洲| 亚欧美中日韩视频| 国产亚洲精品资源在线26u| 久久av一区二区三区| 亚洲免费视频网站| 国产精品丝袜xxxxxxx| 午夜精品福利一区二区三区av | 亚洲精品欧洲| 亚洲国产精品久久久久秋霞不卡| 久色婷婷小香蕉久久| 狠狠综合久久av一区二区老牛| 狂野欧美激情性xxxx| 欧美永久精品| 国产欧美一二三区| 欧美在线观看网站| 欧美在线视频一区| 亚洲电影在线看| 亚洲欧洲一区二区三区| 欧美三级电影一区| 欧美在线日韩在线| 久久精品人人做人人爽电影蜜月 | 欧美一级视频免费在线观看| 亚洲欧美国产不卡| 国内成+人亚洲| 欧美成人激情视频免费观看| 欧美激情1区| 亚洲一区二区精品视频| 亚洲在线播放| 亚洲第一偷拍| 亚洲激情在线激情| 亚洲综合社区| 亚洲国产精品免费| 亚洲精品之草原avav久久| 国产精品久久午夜夜伦鲁鲁| 看片网站欧美日韩| 欧美人与禽猛交乱配视频| 久久精品国产亚洲高清剧情介绍| 裸体一区二区| 性欧美1819sex性高清| 久久亚洲精品视频| 亚洲制服av| 久久综合色天天久久综合图片| 亚洲深夜福利在线| 久久精品一本| 亚洲资源在线观看| 欧美99在线视频观看| 性欧美大战久久久久久久免费观看 | 欧美在线一二三四区| 亚洲黄页一区| 欧美亚洲在线播放| 日韩午夜av| 性感少妇一区| 亚洲一区精品在线| 欧美裸体一区二区三区| 免费亚洲视频| 国产欧美日韩视频一区二区三区| 亚洲激情校园春色| 亚洲国产精品第一区二区三区| 亚洲欧美日韩另类| 99v久久综合狠狠综合久久| 亚洲综合国产精品| 一区二区成人精品| 久热国产精品| 久久精品国产成人| 欧美女人交a| 欧美大成色www永久网站婷| 国产精品视频第一区| 一区二区免费在线播放| 亚洲精品乱码| 久热精品在线| 欧美激情久久久久| 亚洲国产一区二区三区a毛片| 欧美一区二区在线播放| 欧美在线观看一区| 国产精品一级久久久| 99在线观看免费视频精品观看| 亚洲精品久久嫩草网站秘色| 欧美成人自拍| 亚洲成色777777在线观看影院| 1024亚洲| 久久综合网络一区二区| 欧美国产视频一区二区| 狠狠狠色丁香婷婷综合激情| 香蕉亚洲视频| 欧美一级黄色录像| 国产欧美精品国产国产专区| 亚洲一区免费网站| 亚洲色诱最新| 国产精品久久久久久久久免费樱桃 | 欧美日韩在线电影| 999在线观看精品免费不卡网站| 亚洲免费精彩视频| 欧美日韩直播| 欧美精品激情在线观看| 91久久精品国产91久久性色tv| 亚洲激情偷拍| 欧美va亚洲va香蕉在线| 亚洲国内在线| 这里只有精品电影| 国产精品久久久久久影院8一贰佰| 亚洲一卡二卡三卡四卡五卡| 欧美一区二区三区精品| 国模精品一区二区三区| 蜜臀av性久久久久蜜臀aⅴ四虎| 亚洲人被黑人高潮完整版| 亚洲午夜精品一区二区| 国产伦精品一区二区三| 榴莲视频成人在线观看| 亚洲乱码国产乱码精品精98午夜| 午夜日韩在线| 亚洲国产精品成人综合| 国产精品福利av| 小处雏高清一区二区三区| 欧美激情bt| 亚洲欧美日韩天堂一区二区| 国内精品嫩模av私拍在线观看| 欧美福利在线观看| 午夜欧美电影在线观看| 亚洲国内在线| 午夜久久资源| 黑人一区二区| 欧美日韩国产一区二区| 欧美一区二区大片| 亚洲精品自在久久| 久久影视精品| 亚洲欧美国产毛片在线| 亚洲国产中文字幕在线观看| 国产精品视频内| 欧美日韩成人免费| 久久精彩免费视频| 亚洲永久免费视频| 毛片基地黄久久久久久天堂| 午夜精品福利视频| 亚洲精品日韩在线| 亚洲大片免费看| 国产精品一区二区女厕厕| 欧美黄色精品|