contributed by < Julan-Chu >
linux2021
注意共筆書寫規範:中英文之間用一個半形空白字元區隔。
jserv
感謝老師提醒 已修正排版
使用 WSL2, 目前 perf 工具無法安裝,gnuplot 需要使用 xserver,之後會安裝雙系統
make test make check ./qtest
O(1) insert_tail and size
queue.h
q_new 與 q_free 的過程與 alloc 和 free 的過程有雷同之處, block 相當於 list_ele_t
q_new
q_free
q_insert_head
q_insert_tail
兩者皆相似,需注意的議題
q_remove_head
第一版實作有問題
以上通過17個 testcase,但在手動測試發現錯誤
Steps to reproduce bug
原因為當移除最後一個 node 時,沒有將 tail 指向 NULL
ps: 建議可以增加 test case
q_size
q_reverse
q_sort
應用 merge sort + fast slow pointers 實作
The strlen() function calculates the length of the string pointed to by s, excluding the terminating null byte ('\0').
strcpy本身不會檢查目的地記憶體空間大小,可能會導致資料寫到目的地以外的空間
文字訊息不要用圖片展現,不僅不利於排版,日後也不易檢索文字,更對視覺障礙的朋友不友善。
jserv
感謝老師提醒 已替換
cern 建議使用 strlcpy ( BSD 內建),自行撰寫也很簡單 ps: strlcpy 同時也保證 '\0' termination
malloc 後 newh.next 不為 NULL
c99:
The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.
man malloc:
The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
NULL as false 相關
c99
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.(66 If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
66) The macro ++NULL+= is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.
The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int. The expression !E is equivalent to (0==E).
所以NULL可以視為0,而 if(!NULL) 等同於if(0==NULL)
source: stackoverflow
額外思考: 採用這種語法可以讓code更為簡潔,但golang/C#/java不能將null當成bool判斷使用, 而python/javascript可以,背後的設計概念是什麼或是實作上是否有難度?
merge_sort_two_nodes
Steps
先查看 error message 提示程式碼有問題的部分 0x55e9e0da8401 is located 0 bytes to the right of global variable 'echo' defined in 'console.c:59:13' (0x55e9e0da8400) of size 1
SUMMARY: AddressSanitizer: global-buffer-overflow /home/julian/repos/lab0-c/console.c:307 in do_help_cmd Shadow bytes around the buggy address:
先閱讀相關的 param_ptr struct, report function, 以及調用 echo的地方
可以看到這邊為了符合 add_param 的方法簽章利用 pointer to int 去操作 bool 型別的 echo 變數, 重看錯誤訊息,由 0x55e9e0da8400(echo, bool 1 byte) 開始讀取 4 bytes(int type),在 0x55e9e0da8401(next address to echo) 發生錯誤,將 echo 改為 int 就可以修正這個問題
simulation 變數也有一樣的問題
Steps
看起來都是 linenoiseHistory 相關的問題,前兩個警告都是針對 strdup , strdup 會在函數內部做 malloc ,等仍然需要對回傳的 char* 做 free
檢查 linenoise.c以下程式碼,可以看到 strdup 到 return 之間都沒有針對 linecopy 做 free
新增對 free(linecopy)
再次執行 valgrind ./qtest -f traces/trace-12-malloc.cmd
,可以看到針對strdup的警告已經消失
剩下一個問題,研究中
更新想法,以上述修正 strdup 後,在其他測試出現了寫入 free'd 的錯誤, 目前最新猜測是 history 沒有被 free ,
valgrind --leak-check=full --show-leak-kinds=all -v ./qtest -f ./traces/trace-12-malloc.cmd
"still reachable" means your program is probably ok – it didn't free some memory it could have. This is quite common and often reasonable. Don't use –show-reachable=yes if you don't want to see these reports.
在 qtest.main 中 嘗試呼叫 freeHistory 後產生新的錯誤
目前排除失敗。 思考: history 是該手動 free 的對象嗎, still reachable 是該處理的錯誤嗎?
從 c11 有提到 jump 的部分有 goto 跟 setjmp
C11 6.8.6.1 The goto statment A goto statement causes an unconditional jump to the statement prefixed by the named label in the enclosing function.
C11 7.13 Nonlocal jumps<setjmp.h>
兩者的區別為 :
int setjmp(jmp_buf env);
jmp_buf 當前 function 的資訊
void longjmp(jmp_buf env, int val);
jmp_buf 目標 function 的資訊
longjmp 會傳遞 val 給 setjmp , 可以利用回傳值作判斷, 另外要注意的是, longjmp 無法讓 setjmp 回傳 0 ,需要特別注意
If the return is from a direct invocation, the setjmp macro returns the value zero. If the return is from a call to the longjmp function, the setjmp macro returns a nonzero value.
After longjmp is completed, thread execution continues as if the corresponding invocation of the setjmp macro had just returned the value specified by val. The longjmp function cannot cause the setjmp macro to return the value 0; if val is 0, the setjmp macro returns the value 1.
利用setjmp回傳值做判斷:
先用 for(;;) 模擬 long running web server, server_jmp_buf 與 console_jmp_buf 分別為 web server 跟 console 的 jmp_buf, 在 forloop 內設置 setjmp 以及 longjmp, 同時新增 web command
改寫 run_console 利用全局的 webserver_state 變數,決定是否進行console 跟 server 之間的切換
嘗試失敗
出現 longjmp causes uninitialized stack frame 警告, 查詢資料後發現 longjmp 與 setjmp 記憶的是 stack pointer 並非整個 stack ,所以 longjmp to returned function 會出現上述錯誤,因為stack pointer 指向的 stack 已經消失
在 longjmp 跳回 run_consle 後 start_web_srv 的 stack 被釋放
The stack pointer value stored at any point of the programs execution should not be used any further after the current stack pointer has pointed or still points to a lower stack element than the marked one. At such a point the procedure that contained the setjmp call has already returned. The stack pointer was or is set to a lower level and any intermediate subsequent procedure call might have overwritten the element the saved pointer value originally pointed to. Therefore undetermined behaviour or crashes are likely to occur. http://www.fmc-modeling.org/category/projects/apache/amp/A_5_Longjmp.html
另外查詢資料過程, 發現 gcc 文件有提示,沒有宣告volatile的 automatic variable 在longjmp的行為是未定義的, 可能會有 data corruption 的問題
If you use longjmp, beware of automatic variables. ISO C says that automatic variables that are not declared volatile have undefined values after a longjmp. And this is all GCC promises to do, because it is very difficult to restore register variables correctly, and one of GCC’s features is that it can put variables in registers without your asking it to. https://gcc.gnu.org/onlinedocs/gccint/Interface.html
todo: