# 2020q1 Homework1 (lab0)
contributed by < `a20034294` >
## 實驗環境
* 硬體環境:
* Guest CPU kvm64 QEMU(1:2.11+dfsg-1ubuntu7.23) in Proxmox VE
* Host CPU X5650 2.66G*2
* 2GB RAM
* 作業系統: Ubuntu 18.04.4
* 核心版本: Linux 4.15.0-88-generic
* gcc 版本: 7.4.0-1ubuntu1~18.04.1
## 作業目標
- [x] 在 GitHub 上 fork [lab0-c](https://github.com/sysprog21/lab0-c)
- [x] 參閱 [Git 教學和 GitHub 設定指引](https://hackmd.io/@sysprog/git-with-github) ^附教學影片^
- [x] ==詳細閱讀 [C Programming Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf)== ,依據指示著手修改 `queue.[ch]` 和連帶的檔案,測試後用 Git 管理各項修改,記得也該實作 `q_sort` 函式。
- [x] 在提交程式變更前,務必詳閱 [如何寫好 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/)
- [x] 不用理會 [Autolab](http://www.autolabproject.com/) 和檔案下載的描述,這兩者都是 CMU 專屬
- [x] 修改排序所用的比較函式,變更為 [natural sort](https://github.com/sourcefrog/natsort),在 "simulation" 也該做對應的修改,得以反映出 [natural sort](https://github.com/sourcefrog/natsort) 的使用。
- [x] 除了修改程式,也要編輯「[作業區](https://hackmd.io/@sysprog/linux2020-homework1)」,增添開發紀錄和 GitHub 連結,除了提及你如何逐步達到自動評分程式的要求外,共筆也要涵蓋以下:
- [x] 運用 Valgrind 排除 `qtest` 實作的記憶體錯誤,並透過 Massif 視覺化 "simulation" 過程中的記憶體使用量,需要設計對應的實驗
- [ ] 研讀論文 [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) 及程式實作的原理;
- [ ] 解釋 [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),強化 `qtest` 命令列功能,使其支援單行或多行編輯、歷史命令處理、命令自動補完 (例如輸入 `h` 之後按下 Tab 按鍵,自動接續補上 `elp`,成為完整的 `help` 命令)。除了整合程式碼外,應當說明 [antirez/linenoise](https://github.com/antirez/linenoise) 的運作原理,注意到 [termios](http://man7.org/linux/man-pages/man3/termios.3.html) 的運用
- [ ] 思考如何預設開啟 AddressSanitizer,卻又能滿足效能測試所需 (可能需要兩種以上的 build target)
- [ ] 指出現有程式的缺陷 (提示: 和 [RIO 套件](http://csapp.cs.cmu.edu/2e/ch10-preview.pdf) 有關),嘗試強化並提交 pull request
- [ ] 截止日期:
- [ ] Mar 2, 2020 (含) 之前
> [name=a20034294] 大失敗
- [ ] 越早在 GitHub 上有動態、越早接受 code review,評分越高
## 實作
依照 [C Programming Lab](http://www.cs.cmu.edu/~213/labs/cprogramminglab.pdf) 裡的說明,需要更改以下的 function 以完成作業要求
* q_new
* q_free
* q_insert_head
* q_insert_tail
* q_remove_head
* q_size
* q_reverse
* q_sort
### q_new
新增 `malloc return NULL` 時的例外狀況,以及初始化 queue
```diff
13 queue_t *q_new()
14 {
15 queue_t *q = malloc(sizeof(queue_t));
~ 16
+ 17 if (!q) {
+ 18 return NULL;
+ 19 }
20 q->head = NULL;
+ 21 q->tail = NULL;
+ 22 q->size = 0;
23 return q;
24 }
```
### q_free
將所使用的 string 空間以及 `list_ele_t` 所使用的 node 空間釋放
```diff
27 void q_free(queue_t *q)
28 {
~ 29 // cppcheck-suppress variableScope
~ 30 list_ele_t *p, *tmp;
+ 31
+ 32 if (!q)
+ 33 return;
+ 34
+ 35 p = q->head;
+ 36 while (p) {
+ 37 free(p->value);
+ 38 tmp = p;
+ 39 p = p->next;
+ 40 free(tmp);
+ 41 }
42 free(q);
43 }
```
### q_insert_head
與 `q_insert_tail` 差距不大,只貼其中一個
動態分配記憶體 copy 需要 insert 的 string 然後維護 queue
原本使用 `strcpy()` 進行 copy,但被報 Warnning
```diff
52 bool q_insert_head(queue_t *q, char *s)
53 {
54 list_ele_t *newh;
~ 55 char *tmp_s;
+ 56
+ 57 if (!q)
+ 58 return false;
+ 59
60 newh = malloc(sizeof(list_ele_t));
~ 61 if (!newh)
~ 62 return false;
+ 63
+ 64 tmp_s = malloc(strlen(s) + 1);
+ 65 if (!tmp_s) {
+ 66 free(newh);
+ 67 return false;
+ 68 }
+ 69 strncpy(tmp_s, s, strlen(s) + 1);
+ 70
+ 71 newh->value = tmp_s;
72 newh->next = q->head;
73 q->head = newh;
+ 74 if (!q->size)
+ 75 q->tail = newh;
+ 76 q->size++;
77 return true;
78 }
```
### q_remove_head
利用 `strncpy` 進行 copy 並在尾端補 `\0`
最後釋放記憶體空間
```diff
125 bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
126 {
~ 127 list_ele_t *head;
~ 128
+ 129 if (!q || !q->head)
+ 130 return false;
+ 131
+ 132 head = q->head;
+ 133 if (sp) {
+ 134 strncpy(sp, head->value, bufsize);
+ 135 sp[bufsize - 1] = '\0';
+ 136 }
137 q->head = q->head->next;
+ 138 q->size--;
+ 139 free(head->value);
+ 140 free(head);
141 return true;
142 }
```
### q_size
由於在各個會變動大小的函數內已經維護 size,因此直接 `return q->size`
```diff
148 int q_size(queue_t *q)
149 {
+ 150 if (!q)
+ 151 return 0;
+ 152
+ 153 return q->size;
154 }
```
:::warning
可改寫為:
```cpp
int q_size(queue_t *q)
{
return q ? q->size : 0;
}
```
:notes: jserv
> [name=a20034294]已經修改完成 謝謝老師 [4b2c0a](https://github.com/a20034294/lab0-c/commit/4b2c0ab687bf3311fe45b987b1a111cafa85c883)
:::
### q_reverse
走訪 list 並將 `next` 指標反指
最後將 `head`、`tail` 互換
```diff
163 void q_reverse(queue_t *q)
164 {
~ 165 list_ele_t *p, *prev, *next;
~ 166
+ 167 if (!q || q->size <= 1)
+ 168 return;
+ 169
+ 170 p = q->head;
+ 171 prev = NULL;
+ 172 while (p) {
+ 173 next = p->next;
+ 174 p->next = prev;
+ 175 prev = p;
+ 176 p = next;
+ 177 }
+ 178
+ 179 // swap
+ 180 p = q->head;
+ 181 q->head = q->tail;
+ 182 q->tail = p;
183 }
```
### q_sort
使用 merge sort 由於 code 太長了就不貼了
值得一提的是原本直接引入 [natural sort](https://github.com/sourcefrog/natsort) 作為比較使用,結果 `make test` 出現 `TLE + seg fault`,`make valgrind` 執行可以正常通過,判斷應是太慢導致。
調整 insert 的數量,從 $2e6$ 到 $8e5$ 之後可以通過,認為成因可能是 QEMU + x5650 2.66G 太慢。
突然想到可以用 hash 表,讓他可以在判斷字串不同之後才進行比較。
以下為 hash function
```c
/*
* Get the string hash
* @char* s string
*/
inline void hash(list_ele_t *e)
{
e->hash = 0;
for (int i = 0; i < e->len; ++i) {
e->hash *= 10000007;
e->hash += e->value[i];
}
}
```
然後正常通過,不過這對 $aaaa...aab$、$aaaa...aaa$ 這樣的字串比對沒有幫助,因為 hash 值無法比較大小,用紅黑樹的 [map(c++ STL)](http://www.cplusplus.com/reference/map/map/) 或是 hash map 做 hash 比較結果的 cache 可以顯著提升效率,但是實作太難,也沒有必要就不實作了。
---
未完
```bash
==7135==ERROR: AddressSanitizer: global-buffer-overflow on address 0x55b6cb8c19c0 at pc 0x55b6cb6b1d17 bp 0x7ffdba43b8d0 sp 0x7ffdba43b8c
0
READ of size 4 at 0x55b6cb8c19c0 thread T0
#0 0x55b6cb6b1d16 in do_option_cmd /home/a20034294/lab0-c/console.c:368
#1 0x55b6cb6b0ddc in interpret_cmda /home/a20034294/lab0-c/console.c:220
#2 0x55b6cb6b10cc in interpret_cmd /home/a20034294/lab0-c/console.c:243
#3 0x55b6cb6b1fb6 in cmd_select /home/a20034294/lab0-c/console.c:569
#4 0x55b6cb6b223f in run_console /home/a20034294/lab0-c/console.c:628
#5 0x55b6cb6ad26b in main /home/a20034294/lab0-c/qtest.c:772
#6 0x7fbfe07dfb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
#7 0x55b6cb6ad549 in _start (/home/a20034294/lab0-c/qtest+0x7549)
0x55b6cb8c19c1 is located 0 bytes to the right of global variable 'simulation' defined in 'console.c:20:6' (0x55b6cb8c19c0) of size 1
'simulation' is ascii string ''
SUMMARY: AddressSanitizer: global-buffer-overflow /home/a20034294/lab0-c/console.c:368 in do_option_cmd
```