# 2024q1 Homework1 (lab0) contributed by < `Wufangni` > ## 開發環境 ``` shell $ gcc --version gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Address sizes: 39 bits physical, 48 bits virtual Byte Order: Little Endian CPU(s): 16 On-line CPU(s) list: 0-15 Vendor ID: GenuineIntel Model name: 12th Gen Intel(R) Core(TM) i5-12500H CPU family: 6 Model: 154 Thread(s) per core: 2 Core(s) per socket: 12 Socket(s): 1 Stepping: 3 CPU max MHz: 4500.0000 CPU min MHz: 400.0000 BogoMIPS: 6220.80 ``` ## 針對佇列操作的程式碼實作 ### `q_new` > [commit_0f1697a](<https://github.com/Wufangni/lab0-c/commit/0f1697a110a38c743e86abb1112b0807c2cb8880>) :::danger allocate 的翻譯是「配置」,以區格 dispatch (分發/分配) > 已更改 ::: 先建立指向 `list_head` 結構的 head 指標,並使用 `malloc` 配置一個記憶體空間,最後回傳經由 `INIT_LIST_HEAD` 初始化後的 head 指標。 :::danger 段落中的程式碼標注,使用 1 個 [backtick](https://en.wikipedia.org/wiki/Backtick),而非 3 個。 > 已更改 ::: :::danger 避免過多的中英文混用,已有明確翻譯詞彙者,例如「鏈結串列」(linked list) 和「佇列」(queue),就使用該中文詞彙,英文則留給變數名稱、人名,或者缺乏通用翻譯詞彙的場景。 ::: ### `q_free` > [commit_e34d5f1](<https://github.com/Wufangni/lab0-c/commit/e34d5f1ef1fb3fc3c23b87206753375cb9d0068f>) 利用 `list_for_each_entry_safe` 逐一走訪每個節點,並利用 `q_release_element` 釋放該節點的記憶體空間,最後釋放指向佇列第一個元素的 head 指標。 :::danger 無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利) > 已更改 ::: ### `q_insert_head` > [commit_289d41c](<https://github.com/Wufangni/lab0-c/commit/289d41c5f2fc78f671774a4c35460a12a4d15b85>) :::danger 段落中的程式碼標注,使用 1 個 [backtick](https://en.wikipedia.org/wiki/Backtick),而非 3 個。 > 已更改 ::: 利用 `malloc` 配置記憶體空間給新增節點使用,並將欲新增的字元參數指定給新增節點的 value ,再使用 `list_add` 新增節點到 list 的前端。 ### `q_insert_tail` > [commit_9f62397](<https://github.com/Wufangni/lab0-c/commit/aed93dfb1ad8df5bb85d20f23cacbd69a61de1e8>) :::danger 段落中的程式碼標注,使用 1 個 [backtick](https://en.wikipedia.org/wiki/Backtick),而非 3 個。 > 已更改 ::: 與 `q_insert_head` 大致相同,差別在於 `list_add` 部份改為使用`list_add_tail`。 ### `q_remove_head` > [commit_ad185d6](<https://github.com/Wufangni/lab0-c/commit/ad185d6ed4d9e4c9191b9ab6387b96cea929b0fb>) 利用 `head->next` 建立欲刪除的 `remove_h_list`,先儲存 `remove_h_list` 的值到 `sp` 字元中,再對 `head->next` 所指節點做 `list_del`。 :::danger 段落中的程式碼標注,使用 1 個 [backtick](https://en.wikipedia.org/wiki/Backtick),而非 3 個。 > 已更改 ::: ### `q_remove_tail` 與 `q_remove_head` 大致相同,差別在於 `head->next` 部份改為 `head->prev`。 :::danger 段落中的程式碼標注,使用 1 個 [backtick](https://en.wikipedia.org/wiki/Backtick),而非 3 個。 > 已更改 ::: 實作程式碼在 [commit_ad185d6](<https://github.com/Wufangni/lab0-c/commit/d19396fc2ca837c6c02c1384ec371918b58cfbb0>)。 ### `q_size` 新增 `node_count` 變數計數每個節點數量再做回傳。 實作程式碼在 [commit_ad4ada0](<https://github.com/Wufangni/lab0-c/commit/ad4ada0e1d27309c3716d556d04b5e97318fab18>)。 ### `q_delete_mid` 使用 `q_size` 抓取 list 總節點數,新增 `*cur` 指標,並用 for 迴圈到總節點數的一半逐一往下跳到中間節點。 利用 `*cur` 建立`removed_element` ,使用 `list_del` 斷開 `*cur` 在 list 中的連結,最後用 `free` 釋放 `removed_element`。 實作程式碼在 [commit_3bb6704](<https://github.com/Wufangni/lab0-c/commit/3bb67042fe2ea86cdd983b179877dca63f048da1>)。 ### `q_delete_dup` 新增 `*ori` 和 `*cur` 兩個指標,`*ori` 從第一個節點開始,`*cur` 指向 `*ori` 的下一個節點持續往下跑,直到 `*cur` 指到 head 才結束迴圈。 設置布林值 flag 判斷是否 `*ori` 的 value 為重複值(初始值為 false ),若 `*ori` 和 `*cur` 節點的 value 不相同,且 flag 判斷先前沒有與 `*ori` 重複的值被刪除,則 `*ori` 與 `*cur` 皆往下一跳節點。 ``` c if (strcmp(cur_element->value, ori_element->value) != 0 && flag == false) { ori = cur; cur = cur->next; } ``` `*ori` 和 `*cur` 節點的 value 若相同,則刪除 `*cur` 所指節點,且 `*cur` 往下一跳節點,flag 設為 true 表示目前 `*ori` 所指節點的值有重複。 ``` c } else { flag = true; cur = cur->next; element_t *tmp_element = list_entry(cur->prev, element_t, list); list_del(&tmp_element->list); free(tmp_element->value); free(tmp_element); if (cur == head) { element_t *tmp2_element = list_entry(ori, element_t, list); list_del(&tmp2_element->list); free(tmp2_element->value); free(tmp2_element); break; } ``` `*ori` 和 `*cur` 節點的 value 不相同且 flag 為 true,刪除 `*ori` 所指節點,將 `*ori` 指到與 `*cur` 相同節點,`*cur` 往下一節點繼續執行。 ``` c } else if (strcmp(cur_element->value, ori_element->value) != 0 && flag == true) { element_t *tmp_element = list_entry(ori, element_t, list); list_del(&tmp_element->list); ori = cur; free(tmp_element->value); free(tmp_element); cur = cur->next; flag = false; } ``` 實作程式碼在 [commit_bcbeaf7](<https://github.com/Wufangni/lab0-c/commit/bcbeaf7b677f45a7b756db8f4b29b75dfc4142fa>)。 ### `q_swap` 新增 `*front` 和 `*behind` 兩指標對兩兩節點做互換。 ``` c while (true) { if (front == head || behind == head) break; (front->prev)->next = behind; behind->prev = front->prev; (behind->next)->prev = front; front->next = behind->next; front->prev = behind; behind->next = front; front = front->next; behind = front->next; } ``` 實作程式碼在 [commit_f6ddd47](<https://github.com/Wufangni/lab0-c/commit/f6ddd47f27dd0818c3b6d0549c494f838235f613>)。 ### `q_reverse` 使用 `list_for_each_safe` 對每個節點互換 `*prev` 和 `*next` 指標。 ``` c struct list_head *nex, *s; list_for_each_safe (nex, s, head) { element_t *tmp_element = list_entry(nex->next, element_t, list); nex->next = nex->prev; nex->prev = &tmp_element->list; } struct list_head *tmp_head_next = head->next; head->next = head->prev; head->prev = tmp_head_next; ``` 實作程式碼在 [commit_f6ddd47](<https://github.com/Wufangni/lab0-c/commit/b92088728cb41a80fdb31cd1bf64f5b59dc5baf8>)。 ### `q_reverseK` 新增 `*h` 及 `*t` 兩指標分別指向每組要替換的頭跟尾節點,將此組的頭與尾節點與原先 list 斷開並使用 `q_reverse` 做反轉,連回原本的list後將 `*cur` 指向反傳後在此組節點中最尾端的 `*h`,跑 k 次迴圈後停止。 ``` c struct list_head *h, *t, *cur = head; for (int i = 0; i < (q_size(head) / k); i++) { h = cur->next; t = h; for (int j = 1; j < k; j++) t = t->next; cur->next = t->next; (t->next)->prev = cur; t->next = h; h->prev = t; q_reverse(h); h->next = cur->next; (h->next)->prev = h; t->prev = cur; cur->next = t; cur = h; } ``` 實作程式碼在 [commit_ceb5070](<https://github.com/Wufangni/lab0-c/commit/ceb5070f50e8edf908e5ea17f7cf473e667b948a>)。 ### `q_sort` 先建立一個可回傳 `list_head` 的 function `merge`,用 `*pos1` 及 `*pos2` 分別指向兩個 list 的第一個節點,若 `*pos1` 所指節點值比較小則往下跳一節點再做比較,比較大則刪除 `*pos2` 所指節點並將 `*pos2` 往下一節點移動,直到 `*pos1` 或 `*pos2` 跳至 head 。若此時 `*pos2` 指向的list中尚有節點則從第一個節點逐一加到 `*pos1` 所在 list 最後端。 ``` c struct list_head *merge(struct list_head *head1, struct list_head *head2) { struct list_head *pos1 = head1->next, *pos2 = head2->next, *insert; while (pos1 != head1 && pos2 != head2) { element_t *ele1 = list_entry(pos1, element_t, list); element_t *ele2 = list_entry(pos2, element_t, list); if (strcmp(ele1->value, ele2->value) > 0) { insert = pos2; pos2 = pos2->next; list_del(&ele2->list); (pos1->prev)->next = insert; insert->next = pos1; insert->prev = pos1->prev; pos1->prev = insert; } else { pos1 = pos1->next; } } while (pos2 != head2) { insert = pos2; pos2 = pos2->next; list_del(&list_entry(insert, element_t, list)->list); list_add_tail(&list_entry(insert, element_t, list)->list, head1); } return head1; } ``` 實作出 merge sort 的遞迴 function `merge_divide`,再利用剛創好的 `merge` 合併兩 list 。 ``` c struct list_head *merge_divide(struct list_head *head) { struct list_head *mid = head->next, *mid_prev, new_head; if (q_size(head) <= 1) return head; for (int i = 0; i < q_size(head) / 2; i++) mid = mid->next; struct list_head *new_pos = &new_head; new_pos->next = mid; new_pos->prev = head->prev; (head->prev)->next = new_pos; mid_prev = mid->prev; mid->prev = new_pos; head->prev = mid_prev; mid_prev->next = head; return merge(merge_divide(head), merge_divide(new_pos)); } ``` 實作程式碼在 [commit_aed93df](<https://github.com/Wufangni/lab0-c/commit/aed93dfb1ad8df5bb85d20f23cacbd69a61de1e8>)。 ### `q_ascend` / `q_descend` `q_ascend` 對每個節點皆往下逐一檢查,若此節點往下有比它的值還小的節點存在則刪除此節點,且換下一節點檢查。 `q_dscend` 與 `q_ascend` 大致一樣,唯一差別在於 `q_dscend` 尋找比它大的節點,找到才做刪除。 ``` c for (int i = 0; i < len; i++) { bool delete = false; if (cur->next == head) break; struct list_head *node_ = cur->next; element_t *cur_element = list_entry(cur, element_t, list); for (int j = i + 1; j < len; j++) { element_t *nod_element = list_entry(node_, element_t, list); if (strcmp(cur_element->value, nod_element->value) < 0) { cur = cur->next; list_del(&cur_element->list); free(cur_element->value); free(cur_element); delete = true; break; } node_ = node_->next; } if (delete == false) cur = cur->next; } ``` `q_ascend` 實作程式碼在 [commit_5008760](<https://github.com/Wufangni/lab0-c/commit/50087600b8952fd86a62a440ff0f4eb52a3d7cbb>)。 `q_dscend` 實作程式碼在 [commit_8e8a14a](<https://github.com/Wufangni/lab0-c/commit/8e8a14a91c317b9eacb58b9e7a19a2b3066d1a10>)。 ### `q_merge` > [commit_aed93df](<https://github.com/sysprog21/lab0-c/commit/aed93dfb1ad8df5bb85d20f23cacbd69a61de1e8>) :::danger 對照 Dictionary.com 對 [implement](https://www.dictionary.com/browse/implement) 一詞的解釋: * to fulfill; perform; carry out: * to put into effect according to or by means of a definite plan or procedure. * Computers. to realize or instantiate (an element in a program), often under certain conditions as specified by the software involved. * to fill out or supplement 「實作」會是符合上述 "implement" 語意的譯詞,取「起」以「創作」的意思,而「製造」本質上是固定輸入並通常產生固定輸出的行為。 $\to$ [資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary) ::: 實做 ```merge_two_list``` ,作法與上述提到的 ```merge``` 相似,差別在於沒有回傳值。 利用 ```head``` 指向每個 ```queue_contex_t``` 位置串列,將第一個 ```queue_contex_t``` 包含的 ```q``` 分別與每個 ```queue_contex_t->q``` 做 merge。 ``` c int q_merge(struct list_head *head, bool descend) { if (!head || list_empty(head)) return 0; struct list_head *first_q = head->next, *next_q = first_q->next; queue_contex_t *first_queue = list_entry(first_q, queue_contex_t, chain); if (descend == true) q_reverse(first_queue->q); while (next_q != head) { queue_contex_t *next_queue = list_entry(next_q, queue_contex_t, chain); if (descend == true) q_reverse(next_queue->q); merge_two_list(first_queue->q, next_queue->q); next_q = next_q->next; } return q_size(first_queue->q); } ``` :::danger 你如何確認目前的測試程式已涵蓋排序演算法的最差狀況? ::: --- ## 研讀並引入 `lib/list_sort.c` :::danger command 的翻譯是「命令」,而非「指令」(instruction),這裡其實不是「命令」,而是編譯器提供的選項 (option),查閱 GCC 手冊。 > 好的,謝謝老師 。 ::: `__attribute__` 屬於 GCC 的一種編譯器命令,可用來指示編譯器進行一些高階處理和檢查工作,查閱 GCC 手冊得知 `nonnull(2,3,4)` 指示第 2 、 3 、 4 不能為空值。 ``` c __attribute__((nonnull(2,3,4))) ``` `*priv` 用來記錄傳入的 cmp function回傳的值,若 `cmp(priv, a, b) <= 0` 則表示 a < b ,將 a 加入尾端並把 `*a` 往後移一節,若 `*a` 為空串列則回 `*b` 做排序。 :::danger 段落中的程式碼標注,使用 1 個 [backtick](https://en.wikipedia.org/wiki/Backtick),而非 3 個。 > 已更改 ::: ``` c static struct list_head *merge(void *priv, list_cmp_func_t cmp, struct list_head *a, struct list_head *b) { struct list_head *head, **tail = &head; for (;;) { /* if equal, take 'a' -- important for sort stability */ if (cmp(priv, a, b) <= 0) { *tail = a; tail = &a->next; a = a->next; if (!a) { *tail = b; break; } } else { *tail = b; tail = &b->next; b = b->next; if (!b) { *tail = a; break; } } } return head; } ``` 先排除 list 為空值或只有一個節點的情況,再利用 `head->prev->next` 將 list 轉為單向鏈結串列。 ``` c struct list_head *list = head->next, *pending = NULL; size_t count = 0; /* Count of pending */ if (list == head->prev) /* Zero or one elements */ return; /* Convert to a null-terminated singly-linked list. */ head->prev->next = NULL; ``` 將 list_sort.c 及 list_sort.h 引入 lab0-c ,在 qutest.c 中用 `extern` 外部宣告 `list_sort` 函式。 ```diff= +typedef int + __attribute__((nonnull(2, 3))) (*list_cmp_func_t)(void *, + const struct list_head *, + const struct list_head *); +extern void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp); ``` 定義 `cmp` 函式減少後續重複程式碼使用。 ```diff= +int cmp(void *priv, const struct list_head *a, const struct list_head *b) +{ + return strcmp(list_entry(a, element_t, list)->value, + list_entry(b, element_t, list)->value); +} ``` 新增 listsort 命令。 ```diff= ADD_COMMAND(reverse, "Reverse queue", ""); ADD_COMMAND(sort, "Sort queue in ascending/descening order", ""); + ADD_COMMAND(listsort, "Sort queue in ascending/descening order", ""); ADD_COMMAND(size, "Compute queue size n times (default: n == 1)", "[n]"); ADD_COMMAND(show, "Show queue contents", ""); ADD_COMMAND(dm, "Delete middle node in queue", ""); ``` 修改 Makefile。 ```diff= OBJS := qtest.o report.o console.o harness.o queue.o \ random.o dudect/constant.o dudect/fixture.o dudect/ttest.o \ shannon_entropy.o \ - linenoise.o web.o + linenoise.o web.o list_sort.o deps := $(OBJS:%.o=.%.o.d) ``` 詳細程式碼在 [commit_b8e0681](<https://github.com/Wufangni/lab0-c/commit/b8e0681edeac9fb56a32106fe5d7f0bfcc215a0f>)。 ## 提供新的命令 `shuffle` 參考 [Fisher–Yates shuffle](<https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm>) 實做洗牌演算法,新增 `q_shuffle` 函式根據下面步驟進行: 1. 確認鏈節串列不為空值。 2. 使用 `q_size` 計算鍊結串列長度,並用 for 迴圈從最尾端依序向前做交換。 3. 從目前要交換的節點之前找出隨機節點,使用 `srand(time(NULL))` 更改 `seed` 避免每次找隨機值結果不變。 4. 利用 `swap` 交換兩節點,直到 `*last` 指向第二節點時停止迴圈。 ``` c void q_shuffle(struct list_head *head) { if (!head || list_empty(head)) return; struct list_head *last = head->prev; for (int len = q_size(head); len > 1; len--) { srand(time(NULL)); int random_idx = rand() % len; struct list_head *random_node = head->next; while (random_idx--) random_node = random_node->next; swap(last, random_node); last = last->prev; } } ``` 使用 `ADD_COMMAND` 新增 `shuffle` 命令。 ```diff= ADD_COMMAND(reverse, "Reverse queue", ""); ADD_COMMAND(sort, "Sort queue in ascending/descening order", ""); ADD_COMMAND(listsort, "Sort queue in ascending/descening order", ""); + ADD_COMMAND(shuffle, "Shuffle the queue using Fisher-Yates Shuffle", ""); ADD_COMMAND(size, "Compute queue size n times (default: n == 1)", "[n]"); ADD_COMMAND(show, "Show queue contents", ""); ADD_COMMAND(dm, "Delete middle node in queue", ""); ``` 修改 Makefile。 ```diff= OBJS := qtest.o report.o console.o harness.o queue.o \ random.o dudect/constant.o dudect/fixture.o dudect/ttest.o \ shannon_entropy.o \ - linenoise.o web.o list_sort.o + linenoise.o web.o list_sort.o shuffle.o \ deps := $(OBJS:%.o=.%.o.d) ``` 詳細程式碼在[commit_adfc198](<https://github.com/Wufangni/lab0-c/commit/adfc19869c4d31a0446df73415a9f7dbf0906b38>)。 ## 研讀論文〈Dude, is my code constant time?〉 `Timing attacks` 是一種廣泛的 `side-channel attacks` ,不同於直接使用密碼學演算上的攻擊手段,他是利用系統執行時間的特性進行破解。舉例來說,在判斷一段密碼是否與原密碼相符時,若是從第一個字母逐一比對遇到不符合再回傳錯誤警告,就可利用回傳時間來判斷前面正確的字符有幾個,進而產生資安疑慮。 有多數研究和偵測執行可變時間的方法被開發出來,例如 `ctgrind` 、`Flow-tracker` 或 `ctverif` ,大多需要對硬體進行建模,但困難點在於較難取得 CPU 細節資訊,此論文提出 `dudect` 方法可利用統計分析的程式判定是否達成 constant-time ,不用依靠靜態分析可避免硬體上的限制。 ### Step 1: Measure execution time - 針對不同輸入資料重複對執行時間進行觀察,第一種資料通常為固定的值,第二種為隨機產生。 - 現在 CPU 提供週期計數器(Cycle counters)可準確獲取執行時間。 - 最小化環境差異造成的影響,每次測量都會對應隨機產生的輸入分類。 ### Step 2: Apply post-processing 在進入統計分析之前會先對每個單獨的測量值進行一些後處理: - Cropping:可能會因為 OS 或一些外部因素導致時間分佈呈現 positive skew ,這時候可透過裁減 (crop) 掉一些超過 threshold 的測量結果。 - Higher-order preprocessing:使用高階的預處理方式有益於測量,例如`scentered product` 、`mimicking higher-order DPA attack` 。 ### Step 3: Apply statistical test 透過統計檢定來反駁兩執行時間相等的假設,有幾種檢測方式如 `t-test` 及 `Non-parametric tests` 等。 ## `dudect` 從每個 trace 中追蹤第17筆測資會無法通過,觀察內部指令為進入 `simulation` 判斷時造成的問題,回到 `qtest.c` 中觀察程式做 constant time 判斷使用到的兩函式分別為 `is_insert_tail_const()` 及 `is_insert_head_const()` ``` c if (simulation) { if (argc != 1) { report(1, "%s does not need arguments in simulation mode", argv[0]); return false; } bool ok = pos == POS_TAIL ? is_insert_tail_const() : is_insert_head_const(); if (!ok) { report(1, "ERROR: Probably not constant time or wrong implementation"); return false; } report(1, "Probably constant time"); return ok; } ``` 兩函式存放位置在 `fixture.h` 檔案中,裡面定義了 `DUT_FUNCS` 用來測試是否達到 constant time 。 ``` c #ifndef DUDECT_FIXTURE_H #define DUDECT_FIXTURE_H #include <stdbool.h> #include "constant.h" /* Interface to test if function is constant */ #define _(x) bool is_##x##_const(void); DUT_FUNCS #undef _ #endif ``` 到 `fixture.c` 中找到關於 constant time 的實作程式,與 [dudect](<https://github.com/oreparaz/dudect/blob/master/src/dudect.h>) 原始程式碼做比對發現缺漏了部份內容。 ``` c static void prepare_percentiles(dudect_ctx_t *ctx) { qsort(ctx->exec_times, ctx->config->number_measurements, sizeof(int64_t), (int (*)(const void *, const void *))cmp); for (size_t i = 0; i < DUDECT_NUMBER_PERCENTILES; i++) { ctx->percentiles[i] = percentile( ctx->exec_times, 1 - (pow(0.5, 10 * (double)(i + 1) / DUDECT_NUMBER_PERCENTILES)), ctx->config->number_measurements); } } ``` 在 `fixture.c` 補上 `prepare_percentiles` 函式,且因 `prepare_percentiles` 使用了 `DUDECT_NUMBER_PERCENTILES` 定義及其餘函式所以需要一併補齊。 ``` c static int cmp(const int64_t *a, const int64_t *b) { if (*a == *b) return 0; return (*a > *b) ? 1 : -1; } static int64_t percentile(int64_t *a_sorted, double which, size_t size) { size_t array_position = (size_t)((double)size * (double)which); assert(array_position < size); return a_sorted[array_position]; } ``` 在 `doit` 函式中新增剛補上的 `prepare_percentiles` ,且修改有使用到 `DUDECT_NUMBER_PERCENTILES` 定義的 `update_statistics` 參數。 ``` diff= @@ -123,6 +161,7 @@ static bool doit(int mode) int64_t *exec_times = calloc(N_MEASURES, sizeof(int64_t)); uint8_t *classes = calloc(N_MEASURES, sizeof(uint8_t)); uint8_t *input_data = calloc(N_MEASURES * CHUNK_SIZE, sizeof(uint8_t)); + int64_t *percentiles = calloc(DUDECT_NUMBER_PERCENTILES, sizeof(int64_t)); if (!before_ticks || !after_ticks || !exec_times || !classes || !input_data) { @@ -133,7 +172,8 @@ static bool doit(int mode) bool ret = measure(before_ticks, after_ticks, input_data, mode); differentiate(exec_times, before_ticks, after_ticks); - update_statistics(exec_times, classes); + prepare_percentiles(percentiles, exec_times); + update_statistics(exec_times, classes, percentiles); ret &= report(); ``` 再使用 `make test` 測試所有 trace ,可發現所有測試項目都可通過。 ``` fangni@fangni-G5-KF:~/linux2024/lab0-c$ make test scripts/driver.py -c --- Trace Points +++ TESTING trace trace-01-ops: # Test of insert_head and remove_head --- trace-01-ops 5/5 +++ TESTING trace trace-02-ops: # Test of insert_head, insert_tail, remove_head, remove_tail, and delete_mid --- trace-02-ops 6/6 +++ TESTING trace trace-03-ops: # Test of insert_head, insert_tail, remove_head, reverse and merge --- trace-03-ops 6/6 +++ TESTING trace trace-04-ops: # Test of insert_head, insert_tail, size, swap, and sort --- trace-04-ops 6/6 +++ TESTING trace trace-05-ops: # Test of insert_head, insert_tail, remove_head, reverse, size, swap, and sort --- trace-05-ops 6/6 +++ TESTING trace trace-06-ops: # Test of insert_head, insert_tail, delete duplicate, sort, descend and reverseK --- trace-06-ops 6/6 +++ TESTING trace trace-07-string: # Test of truncated strings --- trace-07-string 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-robust: # Test operations on NULL queue --- trace-10-robust 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-malloc: # Test of malloc failure on new --- trace-13-malloc 6/6 +++ TESTING trace trace-14-perf: # Test performance of insert_tail, reverse, and sort --- trace-14-perf 6/6 +++ TESTING trace trace-15-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-15-perf 6/6 +++ TESTING trace trace-16-perf: # Test performance of insert_tail --- trace-16-perf 6/6 +++ TESTING trace trace-17-complexity: # Test if time complexity of q_insert_tail, q_insert_head, q_remove_tail, and q_remove_head is constant Probably constant time Probably constant time Probably constant time Probably constant time --- trace-17-complexity 5/5 --- TOTAL 100/100 ``` 詳細程式碼在 [commit_50da0ff](<https://github.com/Wufangni/lab0-c/commit/50da0ff96351d714fc74f095861b27ceba3c3340>)。