除錯及遞迴函式除錯之思考.
------------------------------------------------------------
author: hjjdebug
date: 2017年 12月 27日
------------------------------------------------------------
除錯之總結(包活正向除錯或逆向除錯)
編寫->編譯->除錯 繼續簡化為編寫->除錯 或者說修改->除錯迴圈.
除錯是修改的基礎,除錯此時佔主導性.
那麼何為除錯,怎麼除錯呢?
除錯是給定一個輸入,必須看到期望的輸出. 此時輸入是已知的,輸出也是已知的,
以此來檢查處理的過程.
除錯的目標:
工作的目標可以有大目標,小目標,但除錯的目標必須是可執行的.除錯到指定位置
期望的輸出通常是一個或一組資料,一個或一組已知的字串.
除錯的目標也可以設定為程式執行的路徑資訊,函式引數資訊等
我也常常在想,到底到底要怎樣除錯,達到怎樣的目的!
除錯的前提:
一般固定的輸入容易獲得, 期望的輸出, 可以通過手工計算,可以通過其它程式計算.
如果有一個正確的路徑可以參考,那自然要利用這個柺杖啦!
除錯的方法:
具體的方法依賴於除錯的環境. 但大體上還是中斷,單步,跟蹤,及列印log等手段
其中在何處中斷,需要技巧.是後續單步的基礎.
有時候,需要構建一個小巧的除錯模型,過濾掉多餘的,不重要的資料,達到簡介除錯的目的
列印log,是一種比較有效率的方法,是計算機與人有效溝通的手段.
靈活運用以上手段,快速解決問題,是除錯能力的判定依據.
IDA 就有一個不錯的偵錯程式,你可以與它對話!
遞迴程式除錯方法:
------------------------------------------------------------
author: hjjdebug
date: 2017年 12月 27日
------------------------------------------------------------
面對一個複雜的被除錯程式,如何才能快速理解其資料目的及目標.
例如,我碰到了一個複雜的遞迴呼叫程式,4個入口,7個出口,7個引數.
這個是個直接的遞迴,呼叫都集中在一個函式裡.
還碰到過間接遞迴,函式呼叫了別的函式,別的函式又反過來呼叫這個函式,
其中分支呼叫函式不記其數. 這種應該是更復雜的.
獨立的分支呼叫不是我們關心的目標,但回過頭來呼叫遞迴的函式是需要考慮的.
遞迴函式易寫難調, 易寫是因為你只需寫退出條件,寫遞迴函式即可.難調是因為
其執行流程不是線性的,當你執行到遞迴函式時,它會逐層展開,形成一個呼叫樹
直到退出條件,再逐層返回,如果是單步跟蹤,跟不了幾步,往往就會騰雲駕霧,不知
自己身處何處.
既然遞迴的呼叫多了一個深度的概念, 好,
手段1,我們用一個glevel變數跟蹤其深度,每遞迴一層,glevel++,
遞迴函式return時, glevel--, 這樣就能定位遞迴函式的深度.
手段2,列印遞迴函式的入口位置,
手段3,列印其它關心的資料.
總之,就是新增列印資訊嗎,通過列印,找到我們關心的資訊和搞通呼叫關係.
為了簡化程式的修改方法,可以使用巨集替代語句.
我為自己找到的這種方法應該說自己想到和實踐的方法而沾沾自喜!
附上2個例項,是我用過的. 留作紀念!
我很少使用巨集,但發現在這個地方使用巨集是非常的方便和有效! 它有效解決了函式呼叫和返回值問題.
#ifdef _USE_MACRO_CALL
#define mac_realScan(a1,a2,e) ({printf("rs in:glevel:%d i_e:%d\n",++glevel,e); \
printf("filename:%s\n",((MapFile *)a1)->filename);\
macRet=realScan(a1,a2); printf("rs rtn:%p\n",macRet);macRet;})
#define mac_rs_rtn(a1,e) ({if(a1){printf("nodes:%d\n",((F2_36*)a1)->list0->node_size);}\
printf("rs out:glevel:%d o_e:%d\n",--glevel,e); return a1;})
#else
#define mac_realScan(a1,a2,e) ({ macRet=realScan(a1,a2); macRet;})
#define mac_rs_rtn(a1,e) ({ return a1;})
#endif