contributed by <jeffcarl67
>
分配一個 queue,要注意 malloc
失敗後的錯誤處理以及 queue_t
結構的初始化
釋放 queue 使用的所有記憶體,此處的作法是每次都刪除第一個元素,直到 linked list 中沒有元素為止
在 queue 的開頭插入一個元素,要比較注意的地方是在用於儲存字串的指針 newh->value
取得記憶體失敗後,在錯誤處理時除了 return false
外還要將先前取得的記憶體一併釋放,才不會有記憶體洩漏的問題
在 queue 的尾端插入一個元素
從 queue 前端移除一個元素,並在參數 char *sp
不為 NULL
的情況下將被刪除元素的字串複製最多 bufsize-1
長度到 char *sp
中
用於取得 queue 中元素總數
在不使用額外分配記憶體的情況下翻轉 queue 中元素的順序,此處使用了三個指針prev
curr
next
,在循環中 curr
從 queue 中第一個元素開始走訪到最後一個元素,每次 prev
指向 curr
前一個元素而 next
指向後一個元素,透過更改這三個元素間的連接達成逆序的目的。在迴圈結束時 curr
指向 NULL
而 prev
指向最後一個元素
在輸入 make test
進行測試時,Makefile 並沒有直接執行 qtest
, 觀察 Makefile 中的規則可以發現, Makefile 以 scripts/driver.py
調用 qtest
進行測試
再看到 scipts/driver.py
中的內容, 其中最主要的程式碼都在 class Tracer
中, 這個 class 負責按照變數 traceDirectory
與 traceDict
中紀錄的測試檔案逐一調用 qtest
進行測試,可以發現變數 traceDirectory
紀錄了測試檔案所在資料夾而 traceDict
中紀錄了測試檔的檔名
在 class Tracer
的函式 run
中, 調用 runTrace
執行單項測試後依據 qtest
的返回值判斷是否通過測試並統計得分
觀察調用 qtest
的函式 runTrace
後可知, 當執行 make test
時, 對於 qtest
的調用為命令 ./qtest -v 1 -f ./traces/trace-XX-XXXX
, 其中 trace-XX-XXXX
為對應的測試檔名, 此命令的意思為設定 verblevel
為 1 並將 trace-XX-XXXX
作為輸入檔案
main
的開頭會先初始化一些區域變數, 緊接著就直接調用 getopt
函式對命令行參數進行處理, 從程式碼中可知 qtest
接受 4 種選項, 分別為 -h
-f
-v
-l
, 功能如下:
資料參考自:
SIGSEGV
及 SIGALRM
兩種信號, SIGSEGV
為發生 segment fault 後產生的信號, SIGALRM
為設定的定時器到時觸發的信號qtest
中用於處理 signal 函式都在 harness.c
中,
為了學習 qtest
中對於 SIGSEGV
, 寫了一個簡單的小程式嘗試使用 SIGSEGV
的 signal handler, 程式碼如下:
然而執行後卻發現 signal handler 被不停調用, 而非如預想中只執行一次, 經過資料搜尋與驗證後得知當 signal handler 返回後回到引發 SIGSEGV
的代碼繼續執行, 因此 SIGSEGV
一直被相同的程式碼觸發, 然而如此無法達到處理 SIGSEGV
的目的, 必須找出辦法在 signal handler 結束後不再執行惠引發錯誤的程式碼, 一種方法就是 qtest
中使用的 sigsetjmp
與 siglongjmp
函數, sigsetjmp
能夠存儲執行環境而 siglongjmp
能跳轉回預先除存的執行環境, 且 sigsetjmp
在直接調用時會返回 0, 在 siglongjmp
跳轉時會返回非 0 值, 在 qtest
中就利用了這個方式:
資料參考自:
https://blog.csdn.net/work_msh/article/details/8470277
http://man7.org/linux/man-pages/man2/signal.2.html
init_cmd
中會調用 add_cmd
與 `add_param`` 函式將一些命令存入一個 linked list 中, 並設定輸入檔案與時間console_init
中會將剩下的所有命令都加入 linked list 中, 至此所有在 qtest
中可用的命令都已經加入了 linked list 中run_console
是 qtest
程式用於處理輸入並且選擇指令執行的程式, 其中最主要執行命令的函式為 interpret_cmda
, 其他相關函式大部分只用於分離命令與參數qtest
最後的收尾工作, 其效果與直接輸入 quit
是一樣的, 而從 main
函式的最後幾行可以發現, 只要在 run_console
與 finish_cmd
中發生一次諸如命令錯誤或記憶體洩漏, qtest
在最後都會返回非 0 值, 代表程式執行中發生錯誤, scripts/driver.py
就是利用這一點進行分數計算在 harness.h
有這一段 macro:
因此在 queue.c 中所有的 malloc
free
strdup
都被替換為 qtest
自己實現的函數, 在分配記憶體時 qtest
會將所有的記憶體放入一個 linked list 中, 並利用 MAGICHEADER
MAGICFREE
MAGICFOOTER
FILLCHAR
紀錄該塊記憶體的狀況,若是已分配的就用 MAGICHEADER
反之用 MAGICFREE
, 並在既已體的末端填入 MAGICFOOTER
且在記憶體剛分配時將payload全部初始化為 FILLCHAR
在檢查是否存在記憶體洩漏或破壞時只須檢測 linked list 中記憶體塊的分配情況與 magic number 是否被改寫就能進行判斷, 例如在函式 test_free
中的這一段程式碼: