# 2018q3 Homework2 (lab0)
Contributed by <`aben20807`>
###### tags: `sysprog2018`
## 開發記錄
### 環境
```
$ uname -a
Linux ben-UX410UQK 4.15.0-23-generic #25-Ubuntu SMP Wed May 23 18:02:16 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0
```
### 結果
:::success
[專案連結](https://github.com/aben20807/lab0-c),已達到 100/100
:::
### 結構
+ 要求:`q_insert_tail()`、`q_size()` 需要 $O(1)$
+ 解決:在結構中加上指向 tail 的 pointer 和 queue 的 size 即可在常數時間存取
### `q_new()`
+ `q` `malloc()` 後必須不是 `NULL` 才有 `head` 成員可以被指向
+ 而不管 `malloc()` 成功與否都要回傳 (失敗回傳 `NULL`)
+ 所以此處最簡潔語法就是當 `q != NULL` 時再去對成員初始化
+ [score]: `trace-10-malloc 7/7`
### malloc 機率性失敗
+ 遇到 `malloc` 失敗時,必須要有對應的處理方式
+ 例如 element 失敗可以直接回傳 `false`
+ 但若是 element 成功,但是 element 的 value 失敗,就需要先把剛剛的 element free 掉才能回傳 `false`
+ 解決:雖然處理例外的行數不多,但還是採用下水道式的設計,利用 `goto` 來做流程控制
### head、tail
+ 在第一個元素被插入時必須注意要把 `head`、`tail` 指向同一個元素,因為該元素是第一個也是最後一個
+ 從尾端加入時要另外注意 `tail == NULL` 的情況,因為是單向 linked list 所以一定要透過 `tail->next` 來插入尾端
### 複製字串
#### 長度
+ `strlen()` 的計算是不包含 `'\0'`
+ e.g. `strlen("abcd")` 是 4
+ 作業要求中的 `bool q_remove_head(queue_t *q, char *sp, size_t bufsize)` 若 `sp` 不為 `NULL` 就要複製 `bufsize - 1` 個字元並在最後加上一個 `'\0'`
#### `strncpy()` 的危險性
+ man page 中警告 `strncpy()` 不會自動在複製完後加上 `'\0'`
+ `char *strncpy(char *dest, const char *src, size_t n);`:從 `src` 複製 `n` 個字元到 `dest`
+ 其中有提及簡單的實作:
```c
char *
strncpy(char *dest, const char *src, size_t n)
{
size_t i;
for (i = 0; i < n && src[i] != '\0'; i++)
dest[i] = src[i];
for ( ; i < n; i++)
dest[i] = '\0';
return dest;
}
```
+ 也就是當原始長度大於想要複製的長度時,目標字串就不會有 `'\0'` 結尾
+ "abcd\0" -4-> "abcd"
+ 一般測試時並不會有異狀,但這是很危險的,尤其是 `malloc` 不保證回傳的記憶體區塊會清空,而這次作業也有出現相關的測試[下方討論](https://hackmd.io/s/Bk_oVbiKm#%E4%B8%8D%E4%B9%BE%E6%B7%A8%E7%9A%84%E5%8D%80%E5%A1%8A)
+ 因此若只使用 `strncpy(sp, rm->value, bufsize - 1);` 會因為沒有 `'\0'` 結尾就印出一堆奇怪的東西
+ 解法1:給定 bufsize - 1 個字元後要在最後加上 `'\0'`
+ 解法2:改用 `snprintf()`
+ `int snprintf(char *str, size_t size, const char *format, ...);`
+ 保證目標會以 `'\0'` 結束
+ "abcd\0" -4-> "abc\0"
### `free(NULL);`
+ 因為 `free` 不是系統的,所以對 `NULL` free 會發生錯誤![[link]](https://github.com/aben20807/lab0-c/blob/master/harness.c#L156-L160)
## 測試程式研究
### `malloc()`
#### 可以追蹤使用者行為
+ 有一個變數 `INTERNAL` [[link]](https://github.com/aben20807/lab0-c/blob/master/harness.h#L14) 是用來隔離呼叫 `malloc()` 的行為
+ 有定義 `INTERNAL` 的地方是使用 glibc 提供的 malloc
+ 沒定義就是呼叫 harness.c 所定義的 malloc,這樣就可以記錄使用者的使用情況甚至可以給定失敗的機率
#### 不乾淨的區塊
+ `malloc` 原本就不保證回傳區塊會是空的,只有 `calloc` 保證
+ 本次作業中實作的 `malloc` 會初始化成非空 [[link]](https://github.com/aben20807/lab0-c/blob/master/harness.c#L172)
+ e.g.
```c
char *s = malloc(30);
printf("%s\n", s);
```
```
UUUUUUUUUUUUUUUUUUUUUUUUUUUUUU����
```
### Test
#### `scripts/driver.py`
+ `$ make test` 會去執行 `scripts/driver.py`
+ `scripts/driver.py -v [0-3]` 可以改變輸出的訊息量
+ 利用 `subprocess` 來執行 /traces 中的 .cmd 檔案 [[link]](https://github.com/aben20807/lab0-c/blob/master/scripts/driver.py#L68),可解讀成
```
$ ./qtest -v [level] -f [cmd file]
```
+ 比較特別的是 verblevel (全名: Verbosity level) 設定,如果用 `$./qtest` level 會是 4 所以在輸入指令時可以看到 queue 中間的內容
#### `harness`
+ 定義自訂的 `malloc` 與 `free`
#### `qtest`
+ 一開始在處理 option 時有一個技巧,以輸入檔為例,宣告檔名變數時使用先給定 NULL,等到有指定的 option (-f xxx.cmd) 時,先將檔名寫進一開始有給空間的 buffer 再賦值給檔名變數,這樣做可以避免使用到 malloc 動態配置。簡易流程如下
```c
char buf[BUFSIZE];
char *infile_name = NULL;
strncpy(buf, optarg, BUFSIZE);
infile_name = buf;
```
+ interpreter 初始化,加入 help、option、time 等指令,是利用 console.c 中的 `cmd_list` 記錄指令以及對應的 callback 函式 [[link]](https://github.com/aben20807/lab0-c/blob/master/console.c#L116),結構如下:
+ 參數 (param) 同樣利用 `param_list`
```c
typedef bool (*cmd_function)(int argc, char *argv[]);
typedef struct CELE cmd_ele, *cmd_ptr;
struct CELE {
char *name;
cmd_function operation;
char *documentation;
cmd_ptr next;
};
```
+ qtest 在印出 queue 時是直接印出 queue 中的內容,而不是另存一個陣列,所以插入會刪除時能夠看到實際結果 [[link]](https://github.com/aben20807/lab0-c/blob/master/qtest.c#L434-L440)
#### `console`
+ [`int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);`](https://github.com/aben20807/lab0-c/blob/master/console.c#L595)
+ monitor multiple file descriptors
+ 可以達到 non-blocking,不必非要等待事件的發生
+ timeout
+ NULL:blocking
+ 0:non-blocking and return immediately
+ 大於 0:blocking until timeout
+ [[Ref] select()函數分析](http://darkmomo.blogspot.com/2008/11/select.html)
## 遭遇問題記錄
#### `[!] cppcheck not installed. Unable to perform static analysis.`
+ `$ sudo apt install cppcheck`