--- tags: linux2022 --- # 2022q1 Homework1 (lab0) contributed by < `oucs638` > ## Test Environment ```shell $ gcc --version gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 39 bits physical, 48 bits virtual CPU(s): 12 On-line CPU(s) list: 0-11 Thread(s) per core: 2 Core(s) per socket: 6 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 158 Model name: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz Stepping: 13 CPU MHz: 3594.185 CPU max MHz: 4500.0000 CPU min MHz: 800.0000 BogoMIPS: 5199.98 Virtualization: VT-x L1d cache: 192 KiB L1i cache: 192 KiB L2 cache: 1.5 MiB L3 cache: 12 MiB NUMA node0 CPU(s): 0-11 ``` ## Implement Progress Records > [Homework Requirement](https://hackmd.io/@sysprog/linux2022-lab0) - There is the Linux-like doubly-linked list implementation in [lab0-c/list.h](https://github.com/oucs638/lab0-c/blob/master/list.h). - Reference: [Linux Kernel API](https://www.kernel.org/doc/html/latest/core-api/kernel-api.html) - Simplify the code with these APIs. ### q_new - Create empty queue. - Return `NULL` if failed to allocate enough memory space. - Initialize queue before returning it. ```c struct list_head *q_new() { // allocate memory space for list head struct list_head *head = malloc(sizeof(struct list_head)); // failed to allocate space if (!head) return NULL; // allocate space success, init it INIT_LIST_HEAD(head); return head; } ``` ### q_free - Iterate over list entries and release every element. - Use `list_for_each_entry_safe(entry, safe, head, member)` to iterate since it will save the next node in temporary storage, so it allows removing the current node. - Invoke `q_release_element(element_t *e)` to free the node. ```c void q_free(struct list_head *l) { if (!l) return; // iterate over list entries and release every element element_t *cur = NULL, *nxt = NULL; list_for_each_entry_safe (cur, nxt, l, list) q_release_element(cur); // release list head free(l); } ``` ### q_insert_head - Allocate memory space for new element. - Allocate memory space for value in new element before copying the string into it. - Invoke `memcpy()` to copy the string into value in new element. - Invoke `list_add()` to add the new node to the beginning of the list. ```c bool q_insert_head(struct list_head *head, char *s) { // return flase if queue if NULL if (!head) return false; // allocate memory space for new queue element // return false if allocate space failed // initialize "list" in new element element_t *new = malloc(sizeof(element_t)); if (!new) return false; INIT_LIST_HEAD(&new->list); // allocate memory space for "value" in new element // allocate one addtional character of memory space for '\0' // return false if allocate space failed size_t len = strlen(s) + 1; new->value = malloc(len * sizeof(char)); if (!new->value) { free(new); return false; } // copy the string into value in new element memcpy(new->value, s, len); // add the new node to the beginning of the list list_add(&new->list, head); return true; } ``` ### q_insert_tail - Similar to `q_insert_head` but `list_add()` is replaced by `list_add_tail()` to add the new node to the end of the list. ```c bool q_insert_tail(struct list_head *head, char *s) { // return flase if queue if NULL if (!head) return false; // allocate memory space for new queue element // return false if allocate space failed // initialize "list" in new element element_t *new = malloc(sizeof(element_t)); if (!new) return false; INIT_LIST_HEAD(&new->list); // allocate memory space for "value" in new element // allocate one addtional character of memory space for '\0' // return false if allocate space failed size_t len = strlen(s) + 1; new->value = malloc(len * sizeof(char)); if (!new->value) { free(new); return false; } // copy the string into value in new element memcpy(new->value, s, len); // add the new node to the end of the list list_add_tail(&new->list, head); return true; } ``` ### q_remove_head - Find the target entry from the list and remove it from the list. - Invoke `list_first_entry()` to get the first entry of the list. - Invoke `list_del_init()` to remove the node from the list. - Invoke `memcpy()` to copy the removed string into `sp`. ```c element_t *q_remove_head(struct list_head *head, char *sp, size_t bufsize) { // return NULL if queue is NULL or empty if (!head || list_empty(head)) return NULL; // get the first entry of the list // and remove it from the list element_t *target = list_first_entry(head, element_t, list); list_del_init(&target->list); // copy the removed string if sp is non-NULL if (sp) { // copy the removed string and plus a null terminator memcpy(sp, target->value, bufsize - 1); sp[bufsize - 1] = '\0'; } return target; } ``` ### q_remove_tail - Similat to `q_insert_head` but `list_first_entry()` is replaced by `list_last_entry()` to get the last entry of the list. ```c element_t *q_remove_tail(struct list_head *head, char *sp, size_t bufsize) { // return NULL if queue if NULL or empty if (!head || list_empty(head)) return NULL; // get the last entry of the list // and remove it from the list element_t *target = list_last_entry(head, element_t, list); list_del(head->prev); // copy the removed string if sp is non-NULL if (sp) { // copy the removed string and plus a null terminator memcpy(sp, target->value, bufsize - 1); sp[bufsize - 1] = '\0'; } return target; } ``` ### q_size - Invoke `list_for_each()` to iterate over list nodes. ```c int q_size(struct list_head *head) { // return 0 if queue is NULL or empty if (!head || list_empty(head)) return 0; int size = 0; // iterate over list nodes and count the number of nodes struct list_head *tmp; list_for_each (tmp, head) size++; return size; } ``` ### q_delete_mid - Iterate from both directions with two iterater to find the middle node. - Invoke `list_entry()` to get the entry of the middle node. - Invoke `list_del_init()` to remove the middle from list. - Invoke `q_release_element()` to free the node. ```c bool q_delete_mid(struct list_head *head) { // return false if queue is NULL or empty if (!head || list_empty(head)) return false; // find the middle node struct list_head *l = head->next, *r = head->prev; while (l != r && l->next != r) { l = l->next; r = r->prev; } // delete the middle node in list element_t *mid = list_entry(r, element_t, list); list_del_init(&mid->list); q_release_element(mid); return true; } ``` ### q_delete_dup - Invoke `list_for_each_entry_safe()` to iterate over list entries because it allow deletes. - Use `flg` to represent whether the current node needs to be deleted or not. - If the value of current node is same as the value of next node, delete current node and set the `flg` to true. ```c bool q_delete_dup(struct list_head *head) { // return false if queue is NULL if (!head) return false; // return true without checking if queue is empty or singular // if (list_empty(head) || list_is_singular(head)) // return true; bool flg = false; element_t *cur = NULL, *nxt = NULL; list_for_each_entry_safe (cur, nxt, head, list) { if ((cur->list.next != head) && (strcmp(cur->value, list_entry(cur->list.next, element_t, list)->value) == 0)) { list_del(&cur->list); q_release_element(cur); flg = true; } else if (flg) { list_del(&cur->list); q_release_element(cur); flg = false; } } return true; } ``` ### q_swap - Do nothing if queue if NULL or empty or singular. - Use two `list_head *` `a` and `b` to implement `swap(a, b)`. ```c= void q_swap(struct list_head *head) { // do nothing if queue is NULL or empty or singular if (!head || list_empty(head) || list_is_singular(head)) return; struct list_head *a = head->next; // check if there are still unprocessed nodes // and more than one node has not been processed while (a != head && a->next != head) { // implement swap (a, b) to swap (a, b) to (b, a) struct list_head *b = a->next; a->next = b->next; b->prev = a->prev; a->prev->next = b; b->next->prev = a; a->prev = b; b->next = a; a = a->next; } } ``` ::: spoiler Explain with diagram - The original state. ```graphviz digraph swap_node { rankdir=LR hd n0 n1 n2 n3 hd -> n0 [label = "next"] n0 -> n1 [label = "next"] n1 -> n2 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n3 [label = "prev"] } ``` - Step 0 : line 12 ```graphviz digraph swap_node { rankdir=LR hd n0 [label = a color = blue] n1 [label = b color = green] n2 n3 hd -> n0 [label = "next"] n0 -> n1 [label = "next"] n1 -> n2 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n3 [label = "prev"] } ``` - Step 1 : line 13 ```graphviz digraph swap_node { rankdir=LR hd n0 [label = a color = blue] n1 [label = b color = green] n2 n3 hd -> n0 [label = "next"] n0 -> n2 [label = "next" color = red] n1 -> n2 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n3 [label = "prev"] } ``` - Step 2 : line 14 ```graphviz digraph swap_node { rankdir=LR hd n0 [label = a color = blue] n1 [label = b color = green] n2 n3 hd -> n0 [label = "next"] n0 -> n2 [label = "next"] n1 -> n2 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n1 [label = "prev"] n1 -> hd [label = "prev" color = red] n0 -> hd [label = "prev"] hd -> n3 [label = "prev"] } ``` - Step 3 : line 15 ```graphviz digraph swap_node { rankdir=LR hd n0 [label = a color = blue] n1 [label = b color = green] n2 n3 hd -> n1 [label = "next" color = red] n0 -> n2 [label = "next"] n1 -> n2 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n1 [label = "prev"] n1 -> hd [label = "prev"] n0 -> hd [label = "prev"] hd -> n3 [label = "prev"] } ``` - Step 4 : line 16 ```graphviz digraph swap_node { rankdir=LR hd n0 [label = a color = blue] n1 [label = b color = green] n2 n3 hd -> n1 [label = "next"] n0 -> n2 [label = "next"] n1 -> n2 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n0 [label = "prev" color = red] n1 -> hd [label = "prev"] n0 -> hd [label = "prev"] hd -> n3 [label = "prev"] } ``` - Step 5 : line 17 ```graphviz digraph swap_node { rankdir=LR hd n0 [label = a color = blue] n1 [label = b color = green] n2 n3 hd -> n1 [label = "next"] n0 -> n2 [label = "next"] n1 -> n2 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n0 [label = "prev"] n1 -> hd [label = "prev"] n0 -> n1 [label = "prev" color = red] hd -> n3 [label = "prev"] } ``` - Step 6 : line 18 ```graphviz digraph swap_node { rankdir=LR hd n0 [label = a color = blue] n1 [label = b color = green] n2 n3 hd -> n1 [label = "next"] n0 -> n2 [label = "next"] n1 -> n0 [label = "next" color = red] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n0 [label = "prev"] n1 -> hd [label = "prev"] n0 -> n1 [label = "prev"] hd -> n3 [label = "prev"] } ``` - Step 7 : line 19 ```graphviz digraph swap_node { rankdir=LR hd n0 n1 n2 [label = a color = blue] n3 [label = b color = green] hd -> n1 [label = "next"] n0 -> n2 [label = "next"] n1 -> n0 [label = "next"] n2 -> n3 [label = "next"] n3 -> hd [label = "next"] n3 -> n2 [label = "prev"] n2 -> n0 [label = "prev"] n1 -> hd [label = "prev"] n0 -> n1 [label = "prev"] hd -> n3 [label = "prev"] } ``` ::: ### q_reverse ```c= void q_reverse(struct list_head *head) { // do nothing if queue is NULL or empty if (!head || list_empty(head)) return; struct list_head *cur = head; // save the original next node of current node first // then make the original prev node into the new next node // and make the origin next node into the new prev node // iterate after done, process all node the same do { struct list_head *nxt = cur->next; cur->next = cur->prev; cur->prev = nxt; cur = nxt; } while (cur != head); } ``` ::: spoiler Explain with diagrams - The original state. ```graphviz digraph reverse_node { rankdir=LR hd n0 n1 n2 hd -> n0 [label = "next"] n0 -> n1 [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n2 [label = "prev"] } ``` - Step 0 : line 14 ```graphviz digraph reverse_node { rankdir=LR hd [label = "cur" color = blue] n0 [label = "nxt" color = green] n1 n2 hd -> n0 [label = "next"] n0 -> n1 [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n2 [label = "prev"] } ``` - Step 1 : 15 ```graphviz digraph reverse_node { rankdir=LR hd [label = "cur" color = blue] n0 [label = "nxt" color = green] n1 n2 hd -> n2 [label = "next"] n0 -> n1 [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n2 [label = "prev"] } ``` - Step 2 : line 16 ```graphviz digraph reverse_node { rankdir=LR hd [label = "cur" color = blue] n0 [label = "nxt" color = green] n1 n2 hd -> n2 [label = "next"] n0 -> n1 [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n0 [label = "prev"] } ``` - Step 3 : line 17 ```graphviz digraph reverse_node { rankdir=LR hd n0 [label = "cur" color = blue] n1 [label = "nxt" color = green] n2 hd -> n2 [label = "next"] n0 -> n1 [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n0 [label = "prev"] } ``` - Repeat the step 1 to step 3 ```graphviz digraph reverse_node { rankdir=LR hd n0 [label = "cur" color = blue] n1 [label = "nxt" color = green] n2 hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> hd [label = "prev"] hd -> n0 [label = "prev"] } ``` ```graphviz digraph reverse_node { rankdir=LR hd n0 [label = "cur" color = blue] n1 [label = "nxt" color = green] n2 hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` ```graphviz digraph reverse_node { rankdir=LR hd n0 n1 [label = "cur" color = blue] n2 [label = "nxt" color = green] hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n2 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` ```graphviz digraph reverse_node { rankdir=LR hd n0 n1 [label = "cur" color = blue] n2 [label = "nxt" color = green] hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n0 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n0 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` ```graphviz digraph reverse_node { rankdir=LR hd n0 n1 [label = "cur" color = blue] n2 [label = "nxt" color = green] hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n0 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n2 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` ```graphviz digraph reverse_node { rankdir=LR hd [label = "nxt" color = green] n0 n1 n2 [label = "cur" color = blue] hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n0 [label = "next"] n2 -> hd [label = "next"] n2 -> n1 [label = "prev"] n1 -> n2 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` ```graphviz digraph reverse_node { rankdir=LR hd [label = "nxt" color = green] n0 n1 n2 [label = "cur" color = blue] hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n0 [label = "next"] n2 -> n1 [label = "next"] n2 -> n1 [label = "prev"] n1 -> n2 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` ```graphviz digraph reverse_node { rankdir=LR hd [label = "nxt" color = green] n0 n1 n2 [label = "cur" color = blue] hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n0 [label = "next"] n2 -> n1 [label = "next"] n2 -> hd [label = "prev"] n1 -> n2 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` - The complete state. ```graphviz digraph reverse_node { rankdir=LR hd n0 n1 n2 hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n0 [label = "next"] n2 -> n1 [label = "next"] n2 -> hd [label = "prev"] n1 -> n2 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` - Equal to: ```graphviz digraph reverse_node { rankdir=LR hd n2 n1 n0 hd -> n2 [label = "next"] n0 -> hd [label = "next"] n1 -> n0 [label = "next"] n2 -> n1 [label = "next"] n2 -> hd [label = "prev"] n1 -> n2 [label = "prev"] n0 -> n1 [label = "prev"] hd -> n0 [label = "prev"] } ``` ::: ### q_sort - Sort the list with merge sort. - The `merge` function refer to [Leetcode NO.23 Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/). - Merge the sorted lists in an iterative way to prevent excessive function call in a recursive way lead to stack overflow. - Refer to [linux: lib/list_sort.c](https://github.com/torvalds/linux/blob/master/lib/list_sort.c) to improve the `merge` function. ```c struct list_head *merge(struct list_head *l1, struct list_head *l2) { if (!l1) return l2; if (!l2) return l1; struct list_head *res = NULL, **cur = &res; while (l1 && l2) { if (strcmp(list_entry(l1, element_t, list)->value, list_entry(l2, element_t, list)->value) > 0) { *cur = l2; cur = &l2->next; l2 = l2->next; } else { *cur = l1; cur = &l1->next; l1 = l1->next; } } if (l1) *cur = l1; if (l2) *cur = l2; return res; } ``` - The `mergeSort` function refer to [Linked List Sort](https://npes87184.github.io/2015-09-12-linkedListSort/). ```c struct list_head *mergeSort(struct list_head *l) { if (!l || !l->next) return l; // split the list struct list_head *fst = l->next, *slw = l; while (fst && fst->next) { slw = slw->next; fst = fst->next->next; } fst = slw->next; slw->next = NULL; return merge(mergeSort(l), mergeSort(fst)); } ``` - Refer to [linux: lib/list_sort.c](https://github.com/torvalds/linux/blob/master/lib/list_sort.c) to treat the list as singly linked list. ```c void q_sort(struct list_head *head) { // do nothing if queue is NULL or empty or singular if (!head || list_empty(head) || list_is_singular(head)) return; // treat the list as a singly linked list head->prev->next = NULL; // sort the list with merge sort head->next = mergeSort(head->next); // recover the list as doubly linked list struct list_head *cur = head; while (cur->next != NULL) { cur->next->prev = cur; cur = cur->next; } head->prev = cur; cur->next = head; } ``` ### q_shuffle - Refer to [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) to implement `do_shuffle` in `q_test.c`. - Pseudo code. ``` -- To shuffle an array a of n elements (indices 0..n-1): for i from n−1 downto 1 do j ← random integer such that 0 ≤ j ≤ i exchange a[j] and a[i] ``` - C code ```c // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle static bool do_shuffle() { if (!l_meta.l) report(3, "Warning: Calling shuffle on null queue"); error_check(); int i = l_meta.size; // for i from n - 1 downto 1 for(struct list_head *ptr1 = l_meta.l->prev; i > 0; i--, ptr1 = ptr1->prev) { struct list_head *ptr2 = l_meta.l->next; // j random integer such that 0 <= j <= i for (int j = rand() % i; j > 0; j--, ptr2 = ptr2->next); // exchange a[i] and a[j] element_t *ele1 = list_entry(ptr1, element_t, list), *ele2 = list_entry(ptr2, element_t, list); char *tmp = ele1->value; ele1->value = ele2->value; ele2->value = tmp; } show_queue(3); return !error_check(); } ``` - Add the command in `console_init()`. ```c ADD_COMMAND( shuffle, " | Shuffle all nodes in queue"); ``` ## Improve the Code Progress Records ### Problems to be solved - Failed the last test. ```shell +++ TESTING trace trace-17-complexity: --- trace-17-complexity 0/5 --- TOTAL 95/100 ``` - But the code works on the local side, so it means `q_insert_head`, `q_insert_tail`, `q_remove_head`, or `q_remove_tail` are not stable. - Profile the `q_test` with valgrind. ```shell make && valgrind ./qtest < traces/trace-17-complexity.cmd ``` :::spoiler Result ```shell make: Nothing to be done for 'all'. Probably constant time ERROR: Probably not constant time Probably constant time Probably constant time ==4162== 32 bytes in 1 blocks are still reachable in loss record 1 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10DB69: init_cmd (console.c:408) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 2 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10DB83: init_cmd (console.c:409) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 3 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10DB9D: init_cmd (console.c:410) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 4 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10DBB7: init_cmd (console.c:411) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 5 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10DBD1: init_cmd (console.c:412) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 6 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10DBEB: init_cmd (console.c:413) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 7 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10DC05: init_cmd (console.c:414) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 8 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C4DF: console_init (qtest.c:767) ==4162== by 0x10C4DF: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 9 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C4F9: console_init (qtest.c:768) ==4162== by 0x10C4F9: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 10 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C513: console_init (qtest.c:769) ==4162== by 0x10C513: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 11 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C52D: console_init (qtest.c:773) ==4162== by 0x10C52D: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 12 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C547: console_init (qtest.c:777) ==4162== by 0x10C547: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 13 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C561: console_init (qtest.c:781) ==4162== by 0x10C561: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 14 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C57B: console_init (qtest.c:785) ==4162== by 0x10C57B: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 15 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C595: console_init (qtest.c:788) ==4162== by 0x10C595: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 16 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C5AF: console_init (qtest.c:789) ==4162== by 0x10C5AF: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 17 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C5C9: console_init (qtest.c:790) ==4162== by 0x10C5C9: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 18 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C5E3: console_init (qtest.c:792) ==4162== by 0x10C5E3: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 19 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C5FD: console_init (qtest.c:793) ==4162== by 0x10C5FD: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 20 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C617: console_init (qtest.c:794) ==4162== by 0x10C617: main (qtest.c:945) ==4162== ==4162== 32 bytes in 1 blocks are still reachable in loss record 21 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D7DF: add_cmd (console.c:89) ==4162== by 0x10C631: console_init (qtest.c:796) ==4162== by 0x10C631: main (qtest.c:945) ==4162== ==4162== 40 bytes in 1 blocks are still reachable in loss record 22 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D859: add_param (console.c:110) ==4162== by 0x10DC24: init_cmd (console.c:415) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 40 bytes in 1 blocks are still reachable in loss record 23 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D859: add_param (console.c:110) ==4162== by 0x10DC43: init_cmd (console.c:416) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 40 bytes in 1 blocks are still reachable in loss record 24 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D859: add_param (console.c:110) ==4162== by 0x10DC62: init_cmd (console.c:417) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 40 bytes in 1 blocks are still reachable in loss record 25 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D859: add_param (console.c:110) ==4162== by 0x10DC81: init_cmd (console.c:418) ==4162== by 0x10C4C5: main (qtest.c:944) ==4162== ==4162== 40 bytes in 1 blocks are still reachable in loss record 26 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D859: add_param (console.c:110) ==4162== by 0x10C650: console_init (qtest.c:798) ==4162== by 0x10C650: main (qtest.c:945) ==4162== ==4162== 40 bytes in 1 blocks are still reachable in loss record 27 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D859: add_param (console.c:110) ==4162== by 0x10C66F: console_init (qtest.c:800) ==4162== by 0x10C66F: main (qtest.c:945) ==4162== ==4162== 40 bytes in 1 blocks are still reachable in loss record 28 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D859: add_param (console.c:110) ==4162== by 0x10C68E: console_init (qtest.c:802) ==4162== by 0x10C68E: main (qtest.c:945) ==4162== ==4162== 52 bytes in 6 blocks are still reachable in loss record 29 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x4A4B50E: strdup (strdup.c:42) ==4162== by 0x110875: linenoiseHistoryAdd (linenoise.c:1236) ==4162== by 0x10E1CF: run_console (console.c:650) ==4162== by 0x10C6E0: main (qtest.c:962) ==4162== ==4162== 104 bytes in 1 blocks are still reachable in loss record 30 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x4A4B50E: strdup (strdup.c:42) ==4162== by 0x110901: linenoiseHistoryAdd (linenoise.c:1236) ==4162== by 0x10E1CF: run_console (console.c:650) ==4162== by 0x10C6E0: main (qtest.c:962) ==4162== ==4162== 160 bytes in 1 blocks are still reachable in loss record 31 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x1108C1: linenoiseHistoryAdd (linenoise.c:1224) ==4162== by 0x10E1CF: run_console (console.c:650) ==4162== by 0x10C6E0: main (qtest.c:962) ==4162== ==4162== 8,216 bytes in 1 blocks are still reachable in loss record 32 of 32 ==4162== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==4162== by 0x10CD55: malloc_or_fail (report.c:189) ==4162== by 0x10D3D8: push_file (console.c:439) ==4162== by 0x10E19B: run_console (console.c:641) ==4162== by 0x10C6E0: main (qtest.c:962) ==4162== ``` ::: - It is found that `q_insert_head()` has a problem after reading the test data. - After a few days of trying, it still get the same error. It is found that the test script may also call `q_sort` a lot, so try to improve the efficiency of `q_sort`. - Replace `memcpy()` with `strncpy()` in `q_remove_head()` and `q_remove_tail()`. - Finally, the code pass the automatic test. [link](https://github.com/oucs638/lab0-c/runs/5345047461?check_suite_focus=true) ```shell +++ TESTING trace trace-17-complexity: --- trace-17-complexity 5/5 --- TOTAL 100/100 ▍▃ ▍▃▅ ▊╷▘▝ ▂▀▆▍╌▘ ╹╷▔▖ ▃▅╷╷▁▗▘ ▁▁ ▝▖│▝▀▅▆▆▅▅▆▆▅╷▁▂▀ ▁▂▃▀▅▀▔▃▔▗ ▍▝▆╷╷╷╷╷╷╷╷╷▝▍ ▂▃▅▆▅╷╷╷╷╷╷╷╷▗ ▕┢╷╷╷╷╷╷╷╷▂▂▁▋▝ ▋▔▃▃▃▃▃▃▃▃▃▃▂▂▁▘ ▏▗▖▃▅╷╸╷┫┗▘▋▁╷▃▀▆▔╋▍╷╷╷╷╷╷╷╷╷╷▗ ▆▅▀▃▎▝▃▘╷▁▂▂▂▖╷▗▔▃▋╷╷╷▗▘╷▂▂▃▀▀▅▅▆▆ ▝▖╷╷╷▌▗▖▝▝▗▅▃▃▎╷▝▀▀▘╷╷▗▖╷╷▎ ▅▂╷▝▅▘╷╷▝▂▂▗┋╷▀┏▅╷▗▘▁▋╷╷▎ ▆▀▂▖▔╷▝╷━▅━╷╷╷▗▃▌▔╊▂▂▃▘ ▅▖▁▏╷╷╷╷╷╷╷╷╷╷▝▅▝▀▀▖ ▊▆╿▔╷▆╷╷╷╷╷╷╷╷╷╷╷▌▘╷▊ ▖╷╷╷╷╷╷╷╷╷╷╷╷╷╷╷▝╺▔ ▝▃╷▀▀▃▃▂▁─╷╷╷╷╷╷▋▋ ▆▅▀▀▃▂▁▅▃▁╷╷▁▂▘ ▆▅▀▖━▌ ▃▊ ``` - However, when I profiled the code with Valgrind, the last test still had some problems. :::spoiler Result ```shell  make && valgrind ./qtest < traces/trace-17-complexity.cmd 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 Probably constant time Probably constant time Probably constant time Probably constant time Freeing queue ==17905== 156 bytes in 7 blocks are still reachable in loss record 1 of 3 ==17905== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==17905== by 0x4A4B50E: strdup (strdup.c:42) ==17905== by 0x110850: linenoiseHistoryAdd (linenoise.c:1236) ==17905== by 0x10E1CF: run_console (console.c:650) ==17905== by 0x10C6E0: main (qtest.c:962) ==17905== ==17905== 160 bytes in 1 blocks are still reachable in loss record 2 of 3 ==17905== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==17905== by 0x11089C: linenoiseHistoryAdd (linenoise.c:1224) ==17905== by 0x11146F: linenoiseHistoryLoad (linenoise.c:1325) ==17905== by 0x10C6B0: main (qtest.c:951) ==17905== ==17905== 528 bytes in 13 blocks are still reachable in loss record 3 of 3 ==17905== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==17905== by 0x4A4B50E: strdup (strdup.c:42) ==17905== by 0x110850: linenoiseHistoryAdd (linenoise.c:1236) ==17905== by 0x11146F: linenoiseHistoryLoad (linenoise.c:1325) ==17905== by 0x10C6B0: main (qtest.c:951) ==17905== ``` ::: ### Fixed the memory error generated during the excution of `q_test()` - After `q_test()` is excuted, the memory allocated by `linenoiseHistoryAdd()` or `linenoiseHistoryLoad()` is not properly released. ```shell $ make && valgrind ./qtest < traces/trace-01-ops.cmd make: Nothing to be done for 'all'. l = [] l = [gerbil] l = [bear gerbil] l = [dolphin bear gerbil] Removed dolphin from queue l = [bear gerbil] Removed bear from queue l = [gerbil] Removed gerbil from queue l = [] Freeing queue ==7332== 131 bytes in 11 blocks are still reachable in loss record 1 of 3 ==7332== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==7332== by 0x4A4B50E: strdup (strdup.c:42) ==7332== by 0x110913: linenoiseHistoryAdd (linenoise.c:1236) ==7332== by 0x10E292: run_console (console.c:650) ==7332== by 0x10C7A3: main (qtest.c:987) ==7332== ==7332== 160 bytes in 1 blocks are still reachable in loss record 2 of 3 ==7332== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==7332== by 0x11095F: linenoiseHistoryAdd (linenoise.c:1224) ==7332== by 0x111532: linenoiseHistoryLoad (linenoise.c:1325) ==7332== by 0x10C773: main (qtest.c:976) ==7332== ==7332== 179 bytes in 9 blocks are still reachable in loss record 3 of 3 ==7332== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==7332== by 0x4A4B50E: strdup (strdup.c:42) ==7332== by 0x110913: linenoiseHistoryAdd (linenoise.c:1236) ==7332== by 0x111532: linenoiseHistoryLoad (linenoise.c:1325) ==7332== by 0x10C773: main (qtest.c:976) ==7332== ``` - The function `linenoiseHistoryAdd()` will called by `run_console()` and `linenoiseHistoryLoad()`. - In `linenoiseHistoryAdd()`, the program will allocate memory for a global variable `static char **history = NULL`. ```c int linenoiseHistoryAdd(const char *line) { char *linecopy; if (history_max_len == 0) return 0; /* Initialization on first call. */ if (history == NULL) { history = malloc(sizeof(char *) * history_max_len); ... } ``` - And there is a funtion named `freeHistory()` will called by `linenoiseAtExit()`. - The function `linenoiseAtExit()` may be registered by `enableRawMode()`. - The function `enableRawMode()` may be called by `linenoiseRaw()`. - The function `linenoiseRaw()` may be called by `linenoise()`. - The only place where it is possible to call `linenoise()` is `run_console()`. ```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) { ... } ``` - If `has_infile` is `true`, `run_console()` will not call `linenoise()`, so `linenoiseAtExit()` will not be registered. - Try to add function to register `linenoiseAtExit()` if `has_infile` is true. Since the global variable `atexit_registered` can not be used out of `linenoise.c`, so it need to create a new function. ```c void addHistoryFree() { if (!atexit_registered) { atexit(linenoiseAtExit); atexit_registered = 1; } } ``` - Try to add the new function. ```c bool run_console(char *infile_name) { ... if (!has_infile) { ... } else { addHistoreyFree(); while (!cmd_done()) cmd_select(0, NULL, NULL, NULL, NULL); } ... } ``` - But it failed. Then I found there may also be cases in `linenoise()` with out registering `linenoiseAtExit()`. - So I try to call `addHistoryFree()` whether `has_infile` is true or not. ```c bool run_console(char *infile_name) { ... addHistoryFree(); if (!has_infile) { ... } else { while (!cmd_done()) cmd_select(0, NULL, NULL, NULL, NULL); } ... } ``` - And it work, after `q_test` is excuted, there has no error message. ```shell $ make && valgrind ./qtest < traces/trace-01-ops.cmd CC console.o CC linenoise.o LD qtest l = [] l = [gerbil] l = [bear gerbil] l = [dolphin bear gerbil] Removed dolphin from queue l = [bear gerbil] Removed bear from queue l = [gerbil] Removed gerbil from queue l = [] Freeing queue ``` - But there will still be an error at the end of the last test. :::spoiler Last Test ```shell  make && valgrind ./qtest < traces/trace-17-complexity.cmd make: Nothing to be done for 'all'. Probably constant time Probably constant time Probably constant time Probably constant time ==11358== 32 bytes in 1 blocks are still reachable in loss record 1 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10DC2C: init_cmd (console.c:408) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 2 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10DC46: init_cmd (console.c:409) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 3 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10DC60: init_cmd (console.c:410) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 4 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10DC7A: init_cmd (console.c:411) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 5 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10DC94: init_cmd (console.c:412) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 6 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10DCAE: init_cmd (console.c:413) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 7 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10DCC8: init_cmd (console.c:414) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 8 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C588: console_init (qtest.c:791) ==11358== by 0x10C588: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 9 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C5A2: console_init (qtest.c:792) ==11358== by 0x10C5A2: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 10 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C5BC: console_init (qtest.c:793) ==11358== by 0x10C5BC: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 11 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C5D6: console_init (qtest.c:797) ==11358== by 0x10C5D6: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 12 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C5F0: console_init (qtest.c:801) ==11358== by 0x10C5F0: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 13 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C60A: console_init (qtest.c:805) ==11358== by 0x10C60A: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 14 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C624: console_init (qtest.c:809) ==11358== by 0x10C624: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 15 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C63E: console_init (qtest.c:812) ==11358== by 0x10C63E: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 16 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C658: console_init (qtest.c:813) ==11358== by 0x10C658: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 17 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C672: console_init (qtest.c:814) ==11358== by 0x10C672: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 18 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C68C: console_init (qtest.c:816) ==11358== by 0x10C68C: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 19 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C6A6: console_init (qtest.c:817) ==11358== by 0x10C6A6: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 20 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C6C0: console_init (qtest.c:818) ==11358== by 0x10C6C0: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 21 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C6DA: console_init (qtest.c:820) ==11358== by 0x10C6DA: main (qtest.c:970) ==11358== ==11358== 32 bytes in 1 blocks are still reachable in loss record 22 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D8A2: add_cmd (console.c:89) ==11358== by 0x10C6F4: console_init (qtest.c:822) ==11358== by 0x10C6F4: main (qtest.c:970) ==11358== ==11358== 40 bytes in 1 blocks are still reachable in loss record 23 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D91C: add_param (console.c:110) ==11358== by 0x10DCE7: init_cmd (console.c:415) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 40 bytes in 1 blocks are still reachable in loss record 24 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D91C: add_param (console.c:110) ==11358== by 0x10DD06: init_cmd (console.c:416) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 40 bytes in 1 blocks are still reachable in loss record 25 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D91C: add_param (console.c:110) ==11358== by 0x10DD25: init_cmd (console.c:417) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 40 bytes in 1 blocks are still reachable in loss record 26 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D91C: add_param (console.c:110) ==11358== by 0x10DD44: init_cmd (console.c:418) ==11358== by 0x10C56E: main (qtest.c:969) ==11358== ==11358== 40 bytes in 1 blocks are still reachable in loss record 27 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D91C: add_param (console.c:110) ==11358== by 0x10C713: console_init (qtest.c:824) ==11358== by 0x10C713: main (qtest.c:970) ==11358== ==11358== 40 bytes in 1 blocks are still reachable in loss record 28 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D91C: add_param (console.c:110) ==11358== by 0x10C732: console_init (qtest.c:826) ==11358== by 0x10C732: main (qtest.c:970) ==11358== ==11358== 40 bytes in 1 blocks are still reachable in loss record 29 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D91C: add_param (console.c:110) ==11358== by 0x10C751: console_init (qtest.c:828) ==11358== by 0x10C751: main (qtest.c:970) ==11358== ==11358== 8,216 bytes in 1 blocks are still reachable in loss record 30 of 30 ==11358== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==11358== by 0x10CE18: malloc_or_fail (report.c:189) ==11358== by 0x10D49B: push_file (console.c:439) ==11358== by 0x10E25E: run_console (console.c:641) ==11358== by 0x10C7A3: main (qtest.c:987) ==11358== ``` ::: - After tracing the code, it found that in `init_cmd()` will allocate memory space for `cmd_list`. - There is a function named `finish_cmd()` that will call `do_quit()` to free the momory of `cmd_list()`. - Refer to [AmyLin](https://hackmd.io/@AmyLin0210/rJpekw9kc)'s note, the `main` function of `q_test` may not call the `finish_cmd()` if the `ok` is false. - So modify a line in `main` of `q_test`. ```c # replace this line ok = ok && finish_cmd(); # with ok = finish_cmd() && ok; ```