# 2021q1 Homework1 (lab0)
contributed by < `jonathanyang0227` >
## Week 1
- [ ] 透過 Computer Systems: A Programmer’s Perspective 學習系統軟體
- [x] 軟體缺失導致的危害
- [x] 解讀計算機編碼
- [x] 你所不知道的 C 語言:指標篇
- [ ] linked list 和非連續記憶體操作
## 作業要求
* 在 GitHub 上 fork [lab0-c](https://github.com/sysprog21/lab0-c)
* 參閱 [Git 教學和 GitHub 設定指引](https://hackmd.io/@sysprog/git-with-github) ^附教學影片^
* ==詳細閱讀 [C Programming Lab](https://www.cs.cmu.edu/afs/cs/academic/class/15213-s20/www/labs/cprogramminglab.pdf)== ,依據指示著手修改 `queue.[ch]` 和連帶的檔案,測試後用 Git 管理各項修改,記得也該實作 `q_sort` 函式。
* 在提交程式變更前,務必詳閱 [如何寫好 Git Commit Message](https://blog.louie.lu/2017/03/21/%E5%A6%82%E4%BD%95%E5%AF%AB%E4%B8%80%E5%80%8B-git-commit-message/)
* 不用理會 [Autolab](http://www.autolabproject.com/) 和檔案下載的描述,這兩者都是 CMU 專屬
* 除了修改程式,也要編輯「[作業區](https://hackmd.io/@sysprog/linux2021-homework1)」,增添開發紀錄和 GitHub 連結,除了提及你如何逐步達到自動評分程式的要求外,共筆也要涵蓋以下:
* 開啟 [Address Sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer),修正 `qtest` 執行過程中的錯誤
* 先執行 `qtest` 再於命令提示列輸入 `help` 命令,會使 開啟 [Address Sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) 觸發錯誤,應予以排除
* 運用 Valgrind 排除 `qtest` 實作的記憶體錯誤,並透過 Massif 視覺化 "simulation" 過程中的記憶體使用量,需要設計對應的實驗
* 在 `qtest` 中實作 [coroutine](https://en.wikipedia.org/wiki/Coroutine),並提供新的命令 `web`,提供 web 伺服器功能,注意: web 伺服器運作過程中,`qtest` 仍可接受其他命令
* 可嘗試整合 [tiny-web-server](https://github.com/7890/tiny-web-server),將 `FORK_COUNT` 變更為 `0`,並以 [coroutine](https://en.wikipedia.org/wiki/Coroutine) 取代原本的 fork 系統呼叫
* 解釋 [select](http://man7.org/linux/man-pages/man2/select.2.html) 系統呼叫在本程式的使用方式,並分析 [console.c](https://github.com/sysprog21/lab0-c/blob/master/console.c) 的實作,說明其中運用 CS:APP [RIO 套件](http://csapp.cs.cmu.edu/2e/ch10-preview.pdf) 的原理和考量點。可對照參考 [CS:APP 第 10 章重點提示](https://hackmd.io/@sysprog/H1TtmVTTz) :arrow_forward: 為避免「舉燭」,請比照 `qtest` 實作,撰寫獨立的終端機命令解譯器 (可建立新的 GitHub repository)
* 說明 [antirez/linenoise](https://github.com/antirez/linenoise) 的運作原理,注意到 [termios](http://man7.org/linux/man-pages/man3/termios.3.html) 的運用
* 研讀論文 [Dude, is my code constant time?](https://eprint.iacr.org/2016/1123.pdf),解釋本程式的 "simulation" 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 [Student's t-distribution](https://en.wikipedia.org/wiki/Student%27s_t-distribution) 及程式實作的原理。注意:現有實作存在若干致命缺陷,請討論並提出解決方案;
* 指出現有程式的缺陷 (提示: 和 [RIO 套件](http://csapp.cs.cmu.edu/2e/ch10-preview.pdf) 有關),嘗試強化並提交 pull request
## queue.c 開發過程
:::warning
以下是我的開發想法跟思路
有:bell:的地方是一開始未注意的地方
:question: 則是帶釐清的地方
:::
### q_new
建立新的「空」佇列。
:::info
1.利用 malloc 產生新的 queue :bell:: malloc 失敗會回傳 NULL
2.依照下列題目要求新增 head, tail, size 並初始化
:::
```cpp
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
/* TODO: What if malloc returned NULL? */
if (!q)
return NULL;
q->size = 0;
q->head = NULL;
q->tail = NULL;
return q;
}
```
:::danger
不要只張貼程式碼,你的「思路」才更關鍵。還記得標題叫做「開發紀錄」吧?
:notes: jserv
:::
>謝謝老師提醒 會盡快補上思路~
### q_free
釋放佇列所佔用的記憶體。
:::info
一一釋放佇列的記憶體
:bell: :最後要記得釋放 queue 本身 另外若先釋放 queue 本身會造成[memory leak](https://en.wikipedia.org/wiki/Memory_leak)
:::
```c
void q_free(queue_t *q)
{
if (!q)
return;
list_ele_t *del;
while (q->head) {
del = q->head->next;
free(q->head->value);
free(q->head);
q->head = del;
}
q->size = 0;
/* Free queue structure */
free(q);
return;
}
```
### q_insert_head
在佇列開頭 (head) 加入 (insert) 給定的新元素 (以 LIFO 準則)。
:::info
在最開頭新增元素
:bell: strncpy()複製字串長度要 +1(結尾符號 '\0')
:bell: malloc完都要記得確認有沒有成功
:bell: 最後要記得檢查 q->tail 是否存在, 若不存在代表先前 q 內並無元素 ,故 tail 為本次新增的元素 (被這個耽誤超久:cry:)
:::
```c
bool q_insert_head(queue_t *q, char *s)
{
list_ele_t *newh;
if (!q)
return false;
newh = malloc(sizeof(list_ele_t));
if(!newh)
return false;
newh->value = malloc(sizeof(char) * (strlen(s) + 1));
if (!newh->value) {
free(newh);
return false;
}
strncpy(newh->value, s, strlen(s) + 1);
newh->next = q->head;
q->head = newh;
q->size++;
if (!q->tail)
q->tail = newh;
return true;
}
```
### q_insert_tail
在佇列尾端 (tail) 加入 (insert) 給定的新元素 (以 FIFO 準則)。
:::info
在尾端新增元素
:bell: 需要檢查 q->head 是否存在,若不存在則此佇列先前並無元素
:question: 成功存入的 newh 以及其 value 不能 free 的原因(因為存入 linklist 內是用 pointer 存取 若 free 掉則該位置無東西可存取)
:::
```cpp
bool q_insert_tail(queue_t *q, char *s)
{
if (!q)
return false;
list_ele_t *newt;
newt = malloc(sizeof(list_ele_t));
if (!newt)
return false;
newt->value = malloc(sizeof(char) * (strlen(s) + 1));
if (!newt->value) {
free(newt);
return false;
}
newt->next = NULL;
strncpy(newt->value, s, strlen(s) + 1);
if (!q->head) {
q->head = newt;
q->tail = newt;
} else {
q->tail->next = newt;
q->tail = newt;
}
q->size++;
return true;
}
```
### q_remove_head
在佇列開頭 (head) 移去 (remove) 給定的元素。
:::info
從佇列開頭開始刪除元素並將元素內的字串存進 sp 中
:bell: 最後複製字串的時候最後要加上結尾符號
:question: 為什麼 bufsize+1 不能改成 strlen(rh->value)+1
:::
```cpp
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
if (!q || !q->head)
return false;
list_ele_t *rh;
rh = q->head;
q->head = q->head->next;
q->size--;
if (sp) {
strncpy(sp, rh->value, bufsize +1);
sp[bufsize - 1] = '\0';
}
free(rh->value);
free(rh);
return true;
}
```
### q_size
計算佇列中的元素數量。
:::info
若 q 不為 NULL 則回傳值
:::
```cpp
int q_size(queue_t *q)
{
if (!q)
return 0;
return q->size;
}
```
### q_reverse
以反向順序重新排列鏈結串列,該函式不該配置或釋放任何鏈結串列元素,換言之,它只能重排已經存在的元素;
:::info
透過逐一走訪 iter ,讓 next 指向 prev ,達到反轉的效果
:bell: 要先在未反轉時先讓 tail=head 以免事後花時間重新找一次
:::
```cpp
void q_reverse(queue_t *q)
{
if (!q || q->size == 1)
return;
list_ele_t *iter = q->head;
list_ele_t *prev = NULL, *next = NULL;
q->tail = q->head;
while (iter != NULL) {
next = iter;
iter = iter->next;
next->next = prev;
prev = next;
}
q->head = next;
}
```
### q_sort
以遞增順序來排序鏈結串列的元素。
:::warning
已稍做經簡化 但人沒有太大差別 考慮用別的方式 :cry:
:::
```cpp
list_ele_t *merge(list_ele_t *l1, list_ele_t *l2)
{
// merge with pseudo node
if (l1 == NULL)
return l2;
if (l2 == NULL)
return l1;
list_ele_t *tmp = NULL;
list_ele_t *head = NULL;
while (l1 && l2) {
if (strcmp(l1->value, l2->value) < 0) {
if(!tmp){
head = l1;
tmp = head;
l1 = l1->next;
}
else{
tmp->next = l1;
l1 = l1->next;
tmp = tmp->next;
}
} else {
if(!tmp){
head = l2;
tmp = head;
l2 = l2->next;
}
else{
tmp->next = l2;
l2 = l2->next;
tmp = tmp->next;
}
}
}
while (l1 || l2) {
if (l1) {
tmp->next = l1;
l1 = l1->next;
} else {
tmp->next = l2;
l2 = l2->next;
}
tmp = tmp->next;
}
return head;
}
list_ele_t *mergeSortList(list_ele_t *head)
{
// merge sort
if (!head || !head->next)
return head;
list_ele_t *fast = head->next;
list_ele_t *slow = head;
// split list
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
fast = slow->next;
slow->next = NULL;
// sort each list
list_ele_t *l1 = mergeSortList(head);
list_ele_t *l2 = mergeSortList(fast);
// merge sorted l1 and sorted l2
return merge(l1, l2);
}
void q_sort(queue_t *q)
{
if (!q || q->size == 1 || !q->head)
return;
q->head = mergeSortList(q->head);
list_ele_t *result = q->head;
while (result->next) {
result = result->next;
}
q->tail = result;
}
```
:::danger
上方 merge sort 實作存在大量改進空間,你應該探討並著手落實。
:notes: jserv
:::
>感謝老師 因為我現在尚有很多待補完整的地方
會盡快進行改進
#### make test結果
```c
joanathan@jonathan:~/linux2021/lab0-c$ make testscripts/driver.py -c
--- Trace Points
+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
--- trace-01-ops 6/6
+++ TESTING trace trace-02-ops:
# Test of insert_head, insert_tail, and remove_head
--- trace-02-ops 6/6
+++ TESTING trace trace-03-ops:
# Test of insert_head, insert_tail, reverse, and remove_head
--- trace-03-ops 6/6
+++ TESTING trace trace-04-ops:
# Test of insert_head, insert_tail, size, and sort
--- trace-04-ops 6/6
+++ TESTING trace trace-05-ops:
# Test of insert_head, insert_tail, remove_head, reverse, size, and sort
--- trace-05-ops 5/5
+++ TESTING trace trace-06-string:
# Test of truncated strings
--- trace-06-string 6/6
+++ TESTING trace trace-07-robust:
# Test operations on NULL queue
--- trace-07-robust 6/6
+++ TESTING trace trace-08-robust:
# Test operations on empty queue
--- trace-08-robust 6/6
+++ TESTING trace trace-09-robust:
# Test remove_head with NULL argument
--- trace-09-robust 6/6
+++ TESTING trace trace-10-malloc:
# Test of malloc failure on new
--- trace-10-malloc 6/6
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
--- trace-11-malloc 6/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
--- trace-12-malloc 6/6
+++ TESTING trace trace-13-perf:
# Test performance of insert_tail
--- trace-13-perf 6/6
+++ TESTING trace trace-14-perf:
# Test performance of size
--- trace-14-perf 6/6
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, reverse, and sort
--- trace-15-perf 6/6
+++ TESTING trace trace-16-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
--- trace-16-perf 6/6
+++ TESTING trace trace-17-complexity:
# Test if q_insert_tail and q_size is constant time complexity
Probably constant time
Probably constant time
--- trace-17-complexity 5/5
--- TOTAL 100/100
```
:::warning
:-1: 有時候會 trace-15-perf 出事應該是 sort 時間太久 待更正
```c
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, reverse, and sort
ERROR: Time limit exceeded. Either you are in an infinite loop, or your code is too inefficient
Segmentation fault occurred. You dereferenced a NULL or invalid pointer
--- trace-15-perf 0/6
```
:::
## 使用 Address Sanitizer 進行檢測
### 使用方法
```c
$ make SANITIZER=1
$ ./qtest
cmd>help
```
:::info
:bell:如果之前就 `make test` 過了,要先 `make clean` 才能使用 `make SANITIZER=1`
:::
### 分析
```c
joanathan@jonathan:~/linux2021/lab0-c$ ./qtest
cmd> help
Commands:
# ... | Display comment
free | Delete queue
help | Show documentation
ih str [n] | Insert string str at head of queue n times. Generate random string(s) if str equals RAND. (default: n == 1)
it str [n] | Insert string str at tail of queue n times. Generate random string(s) if str equals RAND. (default: n == 1)
log file | Copy output to file
new | Create new queue
option [name val] | Display or set options
quit | Exit program
reverse | Reverse queue
rh [str] | Remove from head of queue. Optionally compare to expected value str
rhq | Remove from head of queue without reporting value.
show | Show queue contents
size [n] | Compute queue size n times (default: n == 1)
sort | Sort queue in ascending order
source file | Read commands from source file
time cmd arg ... | Time command execution
Options:
=================================================================
==17298==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5647af6f53c0 at pc 0x5647af6de7bd bp 0x7ffea46d0620 sp 0x7ffea46d0610
READ of size 4 at 0x5647af6f53c0 thread T0
#0 0x5647af6de7bc in do_help_cmd /home/joanathan/linux2021/lab0-c/console.c:307
#1 0x5647af6de8d0 in interpret_cmda /home/joanathan/linux2021/lab0-c/console.c:221
#2 0x5647af6df0b5 in interpret_cmd /home/joanathan/linux2021/lab0-c/console.c:244
#3 0x5647af6e07f8 in run_console /home/joanathan/linux2021/lab0-c/console.c:660
#4 0x5647af6dd3e5 in main /home/joanathan/linux2021/lab0-c/qtest.c:780
#5 0x7f09bbf920b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#6 0x5647af6dab8d in _start (/home/joanathan/linux2021/lab0-c/qtest+0x8b8d)
0x5647af6f53c1 is located 0 bytes to the right of global variable 'echo' defined in 'console.c:59:13' (0x5647af6f53c0) of size 1
SUMMARY: AddressSanitizer: global-buffer-overflow /home/joanathan/linux2021/lab0-c/console.c:307 in do_help_cmd
Shadow bytes around the buggy address:
0x0ac975ed6a20: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
0x0ac975ed6a30: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9
0x0ac975ed6a40: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
0x0ac975ed6a50: 04 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00
0x0ac975ed6a60: 00 00 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
=>0x0ac975ed6a70: 01 f9 f9 f9 f9 f9 f9 f9[01]f9 f9 f9 f9 f9 f9 f9
0x0ac975ed6a80: 04 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9
0x0ac975ed6a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ac975ed6aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ac975ed6ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ac975ed6ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==17298==ABORTING
```
```c
==17298==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5647af6f53c0 at pc 0x5647af6de7bd bp 0x7ffea46d0620 sp 0x7ffea46d0610
READ of size 4 at 0x5647af6f53c0 thread T0
#0 0x5647af6de7bc in do_help_cmd /home/joanathan/linux2021/lab0-c/console.c:307
#1 0x5647af6de8d0 in interpret_cmda /home/joanathan/linux2021/lab0-c/console.c:221
#2 0x5647af6df0b5 in interpret_cmd /home/joanathan/linux2021/lab0-c/console.c:244
#3 0x5647af6e07f8 in run_console /home/joanathan/linux2021/lab0-c/console.c:660
#4 0x5647af6dd3e5 in main /home/joanathan/linux2021/lab0-c/qtest.c:780
#5 0x7f09bbf920b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#6 0x5647af6dab8d in _start (/home/joanathan/linux2021/lab0-c/qtest+0x8b8d)
```
首先上述是在講一個 大小為 4 的 `read` 有 global-buffer-overflow
最明顯的地方就是在 `console.h` 307行那邊有問題
於是去找那行是在講什麼
```c
report(1, "\t%s\t%d\t%s", plist->name, *plist->valp, plist->documentation);
plist = plist->next;
}
```
由於他說大小為 4 故可以判斷應為 `*pist->valp` 這邊出問題,因為 intager 的關係
我們可以追朔到
```c
add_param("echo", (int *) &echo, "Do/don't echo commands", NULL);
```
得知是 echo 這個變數出了問題
```c
static bool echo = 0;
```
我猜想是 echo 是 bool 的型態被強至轉形成 int 產生問題
:::info
expected: int (4 bite)
now: bool (1 bite)
:::
在這裡可能會想直接設定 echo 為 int 就好
於是我再分析一次
```c
joanathan@jonathan:~/linux2021/lab0-c$ ./qtest
cmd> help
Commands:
# ... | Display comment
free | Delete queue
help | Show documentation
ih str [n] | Insert string str at head of queue n times. Generate random string(s) if str equals RAND. (default: n == 1)
it str [n] | Insert string str at tail of queue n times. Generate random string(s) if str equals RAND. (default: n == 1)
log file | Copy output to file
new | Create new queue
option [name val] | Display or set options
quit | Exit program
reverse | Reverse queue
rh [str] | Remove from head of queue. Optionally compare to expected value str
rhq | Remove from head of queue without reporting value.
show | Show queue contents
size [n] | Compute queue size n times (default: n == 1)
sort | Sort queue in ascending order
source file | Read commands from source file
time cmd arg ... | Time command execution
Options:
echo 1 Do/don't echo commands
error 5 Number of errors until exit
fail 30 Number of times allow queue operations to return false
length 1024 Maximum length of displayed string
malloc 0 Malloc failure probability percent
=================================================================
==26154==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5592e6f56a00 at pc 0x5592e6f470b5 bp 0x7fff0b917410 sp 0x7fff0b917400
READ of size 4 at 0x5592e6f56a00 thread T0
#0 0x5592e6f470b4 in do_help_cmd /home/joanathan/linux2021/lab0-c/console.c:307
#1 0x5592e6f471c8 in interpret_cmda /home/joanathan/linux2021/lab0-c/console.c:221
#2 0x5592e6f479ad in interpret_cmd /home/joanathan/linux2021/lab0-c/console.c:244
#3 0x5592e6f490f3 in run_console /home/joanathan/linux2021/lab0-c/console.c:660
#4 0x5592e6f46463 in main /home/joanathan/linux2021/lab0-c/qtest.c:780
#5 0x7f89c32de0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#6 0x5592e6f44acd in _start (/home/joanathan/linux2021/lab0-c/qtest+0x4acd)
0x5592e6f56a01 is located 0 bytes to the right of global variable 'simulation' defined in 'console.c:21:6' (0x5592e6f56a00) of size 1
'simulation' is ascii string ''
SUMMARY: AddressSanitizer: global-buffer-overflow /home/joanathan/linux2021/lab0-c/console.c:307 in do_help_cmd
Shadow bytes around the buggy address:
0x0ab2dcde2cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ab2dcde2d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ab2dcde2d10: 00 00 00 00 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
0x0ab2dcde2d20: 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
0x0ab2dcde2d30: 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
=>0x0ab2dcde2d40:[01]f9 f9 f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00
0x0ab2dcde2d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ab2dcde2d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ab2dcde2d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ab2dcde2d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0ab2dcde2d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==26154==ABORTING
```
看到它寫說 `stimulation` 也有一樣問題
```c
0x5592e6f56a01 is located 0 bytes to the right of global variable 'simulation' defined in 'console.c:21:6' (0x5592e6f56a00) of size 1
'simulation' is ascii string ''
```
於是我將 `stimulation` 也改成 int
```c
int simulation = 0;
```
但是這樣編譯不會過,因為在 `console.h` 中也需要改 `stimulation`
```c
extern int simulation;
```
之後再執行一次
```c
joanathan@jonathan:~/linux2021/lab0-c$ ./qtest
cmd> help
Commands:
# ... | Display comment
free | Delete queue
help | Show documentation
ih str [n] | Insert string str at head of queue n times. Generate random string(s) if str equals RAND. (default: n == 1)
it str [n] | Insert string str at tail of queue n times. Generate random string(s) if str equals RAND. (default: n == 1)
log file | Copy output to file
new | Create new queue
option [name val] | Display or set options
quit | Exit program
reverse | Reverse queue
rh [str] | Remove from head of queue. Optionally compare to expected value str
rhq | Remove from head of queue without reporting value.
show | Show queue contents
size [n] | Compute queue size n times (default: n == 1)
sort | Sort queue in ascending order
source file | Read commands from source file
time cmd arg ... | Time command execution
Options:
echo 1 Do/don't echo commands
error 5 Number of errors until exit
fail 30 Number of times allow queue operations to return false
length 1024 Maximum length of displayed string
malloc 0 Malloc failure probability percent
simulation 0 Start/Stop simulation mode
verbose 4 Verbosity level
```
發現成功了
## 以 Valgrind 分析記憶體問題
### 使用方式
```c
$ make valgrind
```
### 分析
```c
joanathan@jonathan:~/linux2021/lab0-c$ make valgrind
+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
==18190== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18190== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18190== by 0x4A5750E: strdup (strdup.c:42)
==18190== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18190== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18190== by 0x10C234: main (qtest.c:769)
==18190==
==18190== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18190== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18190== by 0x4A5750E: strdup (strdup.c:42)
==18190== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18190== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18190== by 0x10C234: main (qtest.c:769)
==18190==
==18190== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18190== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18190== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18190== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18190== by 0x10C234: main (qtest.c:769)
==18190==
--- trace-01-ops 6/6
+++ TESTING trace trace-02-ops:
# Test of insert_head, insert_tail, and remove_head
==18192== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18192== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18192== by 0x4A5750E: strdup (strdup.c:42)
==18192== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18192== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18192== by 0x10C234: main (qtest.c:769)
==18192==
==18192== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18192== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18192== by 0x4A5750E: strdup (strdup.c:42)
==18192== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18192== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18192== by 0x10C234: main (qtest.c:769)
==18192==
==18192== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18192== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18192== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18192== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18192== by 0x10C234: main (qtest.c:769)
==18192==
--- trace-02-ops 6/6
+++ TESTING trace trace-03-ops:
# Test of insert_head, insert_tail, reverse, and remove_head
==18201== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18201== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18201== by 0x4A5750E: strdup (strdup.c:42)
==18201== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18201== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18201== by 0x10C234: main (qtest.c:769)
==18201==
==18201== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18201== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18201== by 0x4A5750E: strdup (strdup.c:42)
==18201== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18201== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18201== by 0x10C234: main (qtest.c:769)
==18201==
==18201== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18201== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18201== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18201== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18201== by 0x10C234: main (qtest.c:769)
==18201==
--- trace-03-ops 6/6
+++ TESTING trace trace-04-ops:
# Test of insert_head, insert_tail, size, and sort
==18202== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18202== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18202== by 0x4A5750E: strdup (strdup.c:42)
==18202== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18202== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18202== by 0x10C234: main (qtest.c:769)
==18202==
==18202== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18202== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18202== by 0x4A5750E: strdup (strdup.c:42)
==18202== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18202== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18202== by 0x10C234: main (qtest.c:769)
==18202==
==18202== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18202== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18202== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18202== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18202== by 0x10C234: main (qtest.c:769)
==18202==
--- trace-04-ops 6/6
+++ TESTING trace trace-05-ops:
# Test of insert_head, insert_tail, remove_head, reverse, size, and sort
==18203== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18203== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18203== by 0x4A5750E: strdup (strdup.c:42)
==18203== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18203== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18203== by 0x10C234: main (qtest.c:769)
==18203==
==18203== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18203== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18203== by 0x4A5750E: strdup (strdup.c:42)
==18203== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18203== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18203== by 0x10C234: main (qtest.c:769)
==18203==
==18203== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18203== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18203== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18203== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18203== by 0x10C234: main (qtest.c:769)
==18203==
--- trace-05-ops 5/5
+++ TESTING trace trace-06-string:
# Test of truncated strings
==18204== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18204== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18204== by 0x4A5750E: strdup (strdup.c:42)
==18204== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18204== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18204== by 0x10C234: main (qtest.c:769)
==18204==
==18204== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18204== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18204== by 0x4A5750E: strdup (strdup.c:42)
==18204== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18204== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18204== by 0x10C234: main (qtest.c:769)
==18204==
==18204== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18204== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18204== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18204== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18204== by 0x10C234: main (qtest.c:769)
==18204==
--- trace-06-string 6/6
+++ TESTING trace trace-07-robust:
# Test operations on NULL queue
==18207== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18207== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18207== by 0x4A5750E: strdup (strdup.c:42)
==18207== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18207== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18207== by 0x10C234: main (qtest.c:769)
==18207==
==18207== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18207== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18207== by 0x4A5750E: strdup (strdup.c:42)
==18207== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18207== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18207== by 0x10C234: main (qtest.c:769)
==18207==
==18207== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18207== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18207== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18207== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18207== by 0x10C234: main (qtest.c:769)
==18207==
--- trace-07-robust 6/6
+++ TESTING trace trace-08-robust:
# Test operations on empty queue
==18208== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18208== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18208== by 0x4A5750E: strdup (strdup.c:42)
==18208== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18208== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18208== by 0x10C234: main (qtest.c:769)
==18208==
==18208== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18208== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18208== by 0x4A5750E: strdup (strdup.c:42)
==18208== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18208== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18208== by 0x10C234: main (qtest.c:769)
==18208==
==18208== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18208== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18208== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18208== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18208== by 0x10C234: main (qtest.c:769)
==18208==
--- trace-08-robust 6/6
+++ TESTING trace trace-09-robust:
# Test remove_head with NULL argument
==18209== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18209== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18209== by 0x4A5750E: strdup (strdup.c:42)
==18209== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18209== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18209== by 0x10C234: main (qtest.c:769)
==18209==
==18209== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18209== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18209== by 0x4A5750E: strdup (strdup.c:42)
==18209== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18209== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18209== by 0x10C234: main (qtest.c:769)
==18209==
==18209== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18209== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18209== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18209== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18209== by 0x10C234: main (qtest.c:769)
==18209==
--- trace-09-robust 6/6
+++ TESTING trace trace-10-malloc:
# Test of malloc failure on new
==18210== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18210== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18210== by 0x4A5750E: strdup (strdup.c:42)
==18210== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18210== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18210== by 0x10C234: main (qtest.c:769)
==18210==
==18210== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18210== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18210== by 0x4A5750E: strdup (strdup.c:42)
==18210== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18210== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18210== by 0x10C234: main (qtest.c:769)
==18210==
==18210== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18210== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18210== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18210== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18210== by 0x10C234: main (qtest.c:769)
==18210==
--- trace-10-malloc 6/6
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
==18211== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18211== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18211== by 0x4A5750E: strdup (strdup.c:42)
==18211== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18211== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18211== by 0x10C234: main (qtest.c:769)
==18211==
==18211== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18211== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18211== by 0x4A5750E: strdup (strdup.c:42)
==18211== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18211== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18211== by 0x10C234: main (qtest.c:769)
==18211==
==18211== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18211== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18211== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18211== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18211== by 0x10C234: main (qtest.c:769)
==18211==
--- trace-11-malloc 6/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
==18212== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18212== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18212== by 0x4A5750E: strdup (strdup.c:42)
==18212== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18212== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18212== by 0x10C234: main (qtest.c:769)
==18212==
==18212== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18212== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18212== by 0x4A5750E: strdup (strdup.c:42)
==18212== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18212== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18212== by 0x10C234: main (qtest.c:769)
==18212==
==18212== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18212== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18212== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18212== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18212== by 0x10C234: main (qtest.c:769)
==18212==
--- trace-12-malloc 6/6
+++ TESTING trace trace-13-perf:
# Test performance of insert_tail
==18213== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18213== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18213== by 0x4A5750E: strdup (strdup.c:42)
==18213== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18213== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18213== by 0x10C234: main (qtest.c:769)
==18213==
==18213== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18213== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18213== by 0x4A5750E: strdup (strdup.c:42)
==18213== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18213== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18213== by 0x10C234: main (qtest.c:769)
==18213==
==18213== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18213== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18213== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18213== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18213== by 0x10C234: main (qtest.c:769)
==18213==
--- trace-13-perf 6/6
+++ TESTING trace trace-14-perf:
# Test performance of size
==18217== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18217== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18217== by 0x4A5750E: strdup (strdup.c:42)
==18217== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18217== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18217== by 0x10C234: main (qtest.c:769)
==18217==
==18217== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18217== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18217== by 0x4A5750E: strdup (strdup.c:42)
==18217== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18217== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18217== by 0x10C234: main (qtest.c:769)
==18217==
==18217== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18217== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18217== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18217== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18217== by 0x10C234: main (qtest.c:769)
==18217==
--- trace-14-perf 6/6
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, reverse, and sort
==18222== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18222== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18222== by 0x4A5750E: strdup (strdup.c:42)
==18222== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18222== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18222== by 0x10C234: main (qtest.c:769)
==18222==
==18222== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18222== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18222== by 0x4A5750E: strdup (strdup.c:42)
==18222== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18222== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18222== by 0x10C234: main (qtest.c:769)
==18222==
==18222== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18222== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18222== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18222== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18222== by 0x10C234: main (qtest.c:769)
==18222==
--- trace-15-perf 6/6
+++ TESTING trace trace-16-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
==18229== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18229== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18229== by 0x4A5750E: strdup (strdup.c:42)
==18229== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18229== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18229== by 0x10C234: main (qtest.c:769)
==18229==
==18229== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18229== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18229== by 0x4A5750E: strdup (strdup.c:42)
==18229== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18229== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18229== by 0x10C234: main (qtest.c:769)
==18229==
==18229== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18229== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18229== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18229== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18229== by 0x10C234: main (qtest.c:769)
==18229==
--- trace-16-perf 6/6
+++ TESTING trace trace-17-complexity:
# Test if q_insert_tail and q_size is constant time complexity
Probably constant time
Probably constant time
==18230== 7 bytes in 1 blocks are still reachable in loss record 1 of 3
==18230== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18230== by 0x4A5750E: strdup (strdup.c:42)
==18230== by 0x1100A0: linenoiseHistoryAdd (linenoise.c:1236)
==18230== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18230== by 0x10C234: main (qtest.c:769)
==18230==
==18230== 126 bytes in 19 blocks are still reachable in loss record 2 of 3
==18230== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18230== by 0x4A5750E: strdup (strdup.c:42)
==18230== by 0x110014: linenoiseHistoryAdd (linenoise.c:1236)
==18230== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18230== by 0x10C234: main (qtest.c:769)
==18230==
==18230== 160 bytes in 1 blocks are still reachable in loss record 3 of 3
==18230== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==18230== by 0x110060: linenoiseHistoryAdd (linenoise.c:1224)
==18230== by 0x110C33: linenoiseHistoryLoad (linenoise.c:1325)
==18230== by 0x10C234: main (qtest.c:769)
==18230==
--- trace-17-complexity 5/5
--- TOTAL 100/100
Test with specific case by running command:
scripts/driver.py -p /tmp/qtest.cXwndf --valgrind -t <tid>
```
超級多的.....看到這裡真的很崩潰
只好硬著頭皮一個一個看
首先 我們看到每個部份都是有一些東西 still reachable 看起來好像都是在 `qtest.c::769` `linenoise.c:1236` `linenoise.c:1325`
* qtest.c::769
```c
linenoiseHistoryLoad(HISTORY_FILE);
```
* linenoise.c:1236 (在`linenoiseHistoryAdd(const char *line)`內)
```c
linecopy = strdup(line);
```--show-reachable=no
* linenoise.c:1325 (在`linenoiseHistoryLoad(const char *filename)`內)
```c
linenoiseHistoryAdd(buf);
```
我猜測應該問題都出現在 `linenoise.c:1236` 的 `strdup` 中
於是我就google `man strdup`
```c
The strdup() function returns a pointer to a new string which is a duplicate of the string s.
Memory for the new string is obtained with malloc(3), and can be freed with free(3).
```
看起來好像真的是 `strdup()` allocate 的記憶體沒有 free() 掉
我試了兩個方法
* 使用不同的函式複製字串
* 在結尾 free 掉 histroy
但都沒有成功 :cry:,後來參考 `DDerveialm` 大大的筆記,才懂原理
```c=
bool run_console(char *infile_name)
{
if (!push_file(infile_name)) {
report(1, "ERROR: Could not open source file '%s'", infile_name);
return false;
}
if (!has_infile) {
char *cmdline;
while ((cmdline = linenoise(prompt)) != NULL) {
interpret_cmd(cmdline);
linenoiseHistoryAdd(cmdline); /* Add to the history. */
linenoiseHistorySave(HISTORY_FILE); /* Save the history on disk. */
linenoiseFree(cmdline);
}
} else {
while (!cmd_done())
cmd_select(0, NULL, NULL, NULL, NULL);
}
return err_cnt == 0;
}
```
只要避免在讀檔時 free 掉就可以解決的,因此將函式改成下面這樣久可以解決
```c=
if(!infile_name){
linenoiseHistorySetMaxLen(HISTORY_LEN);
linenoiseHistoryLoad(HISTORY_FILE); /* Load the history at startup */
}
```
```c
joanathan@jonathan:~/linux2021/lab0-c$ make valgrind
# Explicitly disable sanitizer(s)
make clean SANITIZER=0 qtest
make[1]: 進入目錄「/home/joanathan/linux2021/lab0-c」
rm -f qtest.o report.o console.o harness.o queue.o random.o dudect/constant.o dudect/fixture.o dudect/ttest.o linenoise.o .qtest.o.d .report.o.d .console.o.d .harness.o.d .queue.o.d .random.o.d .dudect/constant.o.d .dudect/fixture.o.d .dudect/ttest.o.d .linenoise.o.d *~ qtest /tmp/qtest.*
rm -rf .dudect
rm -rf *.dSYM
(cd traces; rm -f *~)
CC qtest.o
CC report.o
CC console.o
CC harness.o
CC queue.o
CC random.o
CC dudect/constant.o
CC dudect/fixture.o
CC dudect/ttest.o
CC linenoise.o
LD qtest
make[1]: 離開目錄「/home/joanathan/linux2021/lab0-c」
cp qtest /tmp/qtest.e3XyB6
chmod u+x /tmp/qtest.e3XyB6
sed -i "s/alarm/isnan/g" /tmp/qtest.e3XyB6
scripts/driver.py -p /tmp/qtest.e3XyB6 --valgrind
--- Trace Points
+++ TESTING trace trace-01-ops:
# Test of insert_head and remove_head
--- trace-01-ops 6/6
+++ TESTING trace trace-02-ops:
# Test of insert_head, insert_tail, and remove_head
--- trace-02-ops 6/6
+++ TESTING trace trace-03-ops:
# Test of insert_head, insert_tail, reverse, and remove_head
--- trace-03-ops 6/6
+++ TESTING trace trace-04-ops:
# Test of insert_head, insert_tail, size, and sort
--- trace-04-ops 6/6
+++ TESTING trace trace-05-ops:
# Test of insert_head, insert_tail, remove_head, reverse, size, and sort
--- trace-05-ops 5/5
+++ TESTING trace trace-06-string:
# Test of truncated strings
--- trace-06-string 6/6
+++ TESTING trace trace-07-robust:
# Test operations on NULL queue
--- trace-07-robust 6/6
+++ TESTING trace trace-08-robust:
# Test operations on empty queue
--- trace-08-robust 6/6
+++ TESTING trace trace-09-robust:
# Test remove_head with NULL argument
--- trace-09-robust 6/6
+++ TESTING trace trace-10-malloc:
# Test of malloc failure on new
--- trace-10-malloc 6/6
+++ TESTING trace trace-11-malloc:
# Test of malloc failure on insert_head
--- trace-11-malloc 6/6
+++ TESTING trace trace-12-malloc:
# Test of malloc failure on insert_tail
--- trace-12-malloc 6/6
+++ TESTING trace trace-13-perf:
# Test performance of insert_tail
--- trace-13-perf 6/6
+++ TESTING trace trace-14-perf:
# Test performance of size
--- trace-14-perf 6/6
+++ TESTING trace trace-15-perf:
# Test performance of insert_tail, size, reverse, and sort
--- trace-15-perf 6/6
+++ TESTING trace trace-16-perf:
# Test performance of sort with random and descending orders
# 10000: all correct sorting algorithms are expected pass
# 50000: sorting algorithms with O(n^2) time complexity are expected failed
# 100000: sorting algorithms with O(nlogn) time complexity are expected pass
--- trace-16-perf 6/6
+++ TESTING trace trace-17-complexity:
# Test if q_insert_tail and q_size is constant time complexity
Probably constant time
Probably constant time
--- trace-17-complexity 5/5
--- TOTAL 100/100
Test with specific case by running command:
scripts/driver.py -p /tmp/qtest.e3XyB6 --valgrind -t <tid>
```
### 透過 Massif 視覺化記憶體使用量
首先先安裝 `massif-visualizer` 跑出圖表
```c
$ sudo apt-get install massif-visualizer
```
再來以 **trace-16-perf.cmd** 為例
```c
valgrind --tool=massif ./qtest -f traces/trace-16-perf.cmd
```
會生成 massif 圖檔,輸入以下指令將其開啟
```c
massif-visualizer massif.out.11729
```
以下就是所生成的圖檔

:::info
TODO massif實驗
:::
## select system call & 分析 console.c
根據[select(2) — Linux manual page](https://man7.org/linux/man-pages/man2/select.2.html)
> select() allows a program to monitor multiple file descriptors,
> waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible).
> A file descriptor is considered ready if it is possible to perform a corresponding I/O operation
:::info
**file descriptors:** 開啟檔案之後都會回一個 file descriptor,之後操作檔案皆須透過此參數
:::
### the use of `select()`
* monitors multiple file descriptors.
* waiting until one or more of the file descriptors become “ready” for some class of I/O operation (e.g., input possible).
### Arguments
* **`nfds`**: This argument should be set to the highest-numbered filedescriptor in any of the three sets, plus 1.
* **`readfds`**: The file descriptors in this set are watched to see if they are ready for reading.
* **`writefds`**: The file descriptors in this set are watched to see if they are ready for writing.
* **`exceptfds`**: The file descriptors in this set are watched for "exceptional conditions".
* **`timeout`**: The timeout argument is a timeval structure (shown below) that specifies the interval that select() should block waiting for a file descriptor to become ready.
> The call will block until either:
>* a file descriptor becomes ready
>* the call is interrupted by a signal handler
>* the timeout expires
### File descriptor sets
* **`FD_ZERO()`**: clears set.(initializing a file descriptor set)
* **`FD_SET()`**: adds the file descriptor fd to set.
* **`FD_CLR()`**: removes the file descriptor from set.
* **`FD_ISSET()`**: tests to see if a file descriptor is part of the set
### return value
* if success, return **the number of file descriptors**
* return **zero** if the timeout expired before any file descriptors became ready.
* **-1** is returned, and errno is set to indicate the error; the file descriptor sets are unmodified, and timeout becomes undefined.
### `console.c` 中 `select()` 的實作
```c
int cmd_select(int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout)
{
int infd;
fd_set local_readset;
if (cmd_done())
return 0;
if (!block_flag) {
/* Process any commands in input buffer */
if (!readfds)
readfds = &local_readset;
/* Add input fd to readset for select */
infd = buf_stack->fd;
FD_SET(infd, readfds);
if (infd == STDIN_FILENO && prompt_flag) {
printf("%s", prompt);
fflush(stdout);
prompt_flag = true;
}
if (infd >= nfds)
nfds = infd + 1;
}
if (nfds == 0)
return 0;
int result = select(nfds, readfds, writefds, exceptfds, timeout);
if (result <= 0)
return result;
infd = buf_stack->fd;
if (readfds && FD_ISSET(infd, readfds)) {
/* Commandline input available */
FD_CLR(infd, readfds);
result--;
if (has_infile) {
char *cmdline;
cmdline = readline();
if (cmdline)
interpret_cmd(cmdline);
}
}
return result;
}
```
在 blog flag = false 的情況下(程式中恆為false 為什麼要設這個)=>後來參考別人筆記得知是殘存的程式碼
```cpp
if (!readfds)
readfds = &local_readset;
```
若 readfds 為 NULL ,則指向 local_readset
```c
infd = buf_stack->fd;
FD_SET(infd, readfds);
```
新增 buffer 中的 file descriptors 新增到 set 中,送到 select()
```c
int result = select(nfds, readfds, writefds, exceptfds, timeout);
if (result <= 0)
return result;
```
select會回傳有多少 file descriptors 準備好,若值<=0 為沒有或有錯誤, return
```c
infd = buf_stack->fd;
if (readfds && FD_ISSET(infd, readfds)) {
/* Commandline input available */
FD_CLR(infd, readfds);
result--;
if (has_infile) {
char *cmdline;
cmdline = readline();
if (cmdline)
interpret_cmd(cmdline);
}
}
```
:::info
接著這邊是對 file descriptor的操作,目前了解操作完之後減少 result 的數量,其餘部份還不清楚
:::
### 說明其中運用 CS:APP RIO 套件 的原理和考量點
TODO
還在研讀cs:app
## 說明 antirez/linenoise 的運作原理
說明 [antirez/linenoise](https://github.com/antirez/linenoise) 的運作原理,注意到 [termios](http://man7.org/linux/man-pages/man3/termios.3.html) 的運用
## 實作 coroutine
[coroutine](https://en.wikipedia.org/wiki/Coroutine)
## 研讀 Dude, is my code constant time?
[Dude, is my code constant time?](https://eprint.iacr.org/2016/1123.pdf),解釋本程式的 "simulation" 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 [Student's t-distribution](https://en.wikipedia.org/wiki/Student%27s_t-distribution)