.. _dcc: ********* dcc ********* .. topic:: Learning Outcome Understand the benefits of dcc and interpret the output of dcc's inbuilt debugging capabilities. .. topic:: Introduction It is recommended that COMP1511 students use dcc. dcc is a compiler that uses the tools :ref:`Valgrind` and :ref:`ASan` to provide extra help to beginner programmers about memory errors and runtime errors. It can even be used to check for memory leaks. .. topic:: Applicable subjects COMP1511 ---- Using dcc ============ dcc compiles and runs in a very similar manner to gcc: :: $ dcc -o $ ./program If your program doesn't have any undefined behaviour or memory errors, it should produce the same output as if you had compiled with gcc. dcc only provides additional output when your program has a variety of issues such as accessing invalid array indicies, and dereferencing a NULL pointer. Examples of how to interpret this output are provided in the examples below. Checking for memory leaks with dcc ========================================= dcc can be used to check for memory leaks (i.e. malloced memory that hasn't been freed). To use this feature, compile your program with - -leak-check: :: $ dcc --leak-check ---- Examples ========= Dereferencing NULL ********************************* When a NULL pointer is dereferenced, there are vast differences between the errors produced by the executables compiled with gcc, compared with those compiled with dcc. dereferencing a NULL pointer .. literalinclude:: broken_linked_list.c :language: c :linenos: :caption: broken_linked_list.c gcc ^^^^^^^ :: $ gcc -o broken_linked_list broken_linked_list.c $ ./broken_linked_list Segmentation fault dcc ^^^^^^^ :: $ dcc -o broken_linked_list broken_linked_list.c $ ./broken_linked_list broken_linked_list.c:51:30: runtime error - accessing a field via a NULL pointer dcc explanation: You are using a pointer which is NULL A common error is using p->field when p == NULL. Execution stopped in print_list(list=0x602000000030, length=7) in broken_linked_list.c at line 51: void print_list(struct node *list, int length){ struct node *curr = list; int i = 0; while (i <= length) { --> printf("%d->", curr->data); curr = curr->next; i++; } Values when execution stopped: curr = NULL i = 7 length = 7 Function Call Traceback print_list(list=0x602000000030, length=7) called at line 18 of broken_linked_list.c main() Compiling and running with gcc only tells us that our program crashed because of a "segmentation fault", which only tells us that we accessed an illegal part of memory. dcc, however, tells us exactly what went wrong: | :: runtime error - accessing a field via a NULL pointer This means that the program encountered an error while running. The error occurred because we used the arrow/stab operator on a NULL pointer (e.g. NULL->data or NULL->next). | :: Execution stopped in print_list(list=0x602000000030, length=7) in broken_linked_list.c at line 51: void print_list(struct node *list, int length){ struct node *curr = list; int i = 0; while (i <= length) { --> printf("%d->", curr->data); curr = curr->next; i++; } These lines mean that the error was encountered on line 51 of broken_linked_list.c. The lines around line 51 are printed out and the line of interest is indicated with an arrow. The data field of the curr pointer is being accessed on this line. We know from previous output, that we are dereferencing a NULL. We now suspect that curr is NULL at this line. | :: Values when execution stopped: curr = NULL i = 7 length = 7 These lines provide the values of our variables when we the error happened. We now know for sure that curr was indeed NULL on line 51. We also know that the segfault occurred when i = 7 (i.e. on iteration 7 of the while loop). However, we should never reach the 7th iteration of the loop, as there are only 7 nodes. We appear to have gone off the end of the list. An off by one error is common and would cause the while loop to go for one more or one less loop than desired. Line 50 stops the loop when i is greater than length (i.e. when i = 8). We want to exit the loop when i = 7, so this is most likely causing our issues. Looking at the code, we may realise that, not only is there an off by one error, but there is a better way to traverse a linked list to its end. This is achieved by ending the loop when a NULL is reached. This adds some protection against an incorrect length passed in. We fix this code with the new function and no more segfault! :: $ ./linked_list.c 0->1->2->3->4->5->6->X .. literalinclude:: linked_list.c :language: c :linenos: :caption: linked_list.c ---- Illegal Array Index Access Example ************************************** Compiling with gcc lets us access illegal array indices, however, compiling with dcc often tells that there is an issue as the code is compiling. .. literalinclude:: illegal_array.c :language: c :linenos: :caption: illegal_array.c gcc ^^^^^^^^^^^ :: $ gcc -o illegal_array illegal_array.c $ ./illegal_array 0 1 2 3 4 5 6 7 8 9 10 dcc ^^^^^^^^^^^ :: $ dcc -o illegal_array illegal_array.c illegal_array.c: In function \u2018main\u2019: illegal_array.c:15:9: warning: iteration 10 invokes undefined behavior [-Waggressive-loop-optimizations] printf("%d ", array[i]); ^~~~~~~~~~~~~~~~~~~~~~~ illegal_array.c:14:11: note: within this loop while (i <= 10) { ^ illegal_array.c:9:18: warning: iteration 10 invokes undefined behavior [-Waggressive-loop-optimizations] array[i] = i; ~~~~~~~~~^~~ illegal_array.c:8:11: note: within this loop while (i <= 10) { In the code provided above, gcc lets us access invalid indicies without any warning (which might not cause a problem in this instance, but it is undefined behaviour and can cause some annoying bugs). dcc, however,picks up that we are reading past the end of an array during compile time. It tells us which lines we need to check as well as which iteration will cause undefined behaviour. | :: illegal_array.c:15:9: warning: iteration 10 invokes undefined behavior [-Waggressive-loop-optimizations] printf("%d ", array[i]); ^~~~~~~~~~~~~~~~~~~~~~~ illegal_array.c:14:11: note: within this loop while (i <= 10) { This tells us that on the 10th iteration of the while loop on line 14, there is "undefined behaviour". The undefined behaviour occurs on line 15 which is an array access. | :: illegal_array.c:9:18: warning: iteration 10 invokes undefined behavior [-Waggressive-loop-optimizations] array[i] = i; ~~~~~~~~~^~~ illegal_array.c:8:11: note: within this loop while (i <= 10) { This tells us that we have the same issue on line 9 as we did on line 15. An array access which causes undefined behaviour. | We then consider what occurs on iteration 10 and realise that our array is only of length 10 - there shouldn't be a 10th iteration. There is an off by one error caused by the *<=* operator in both loops. | :: $ dcc -o illegal_array illegal_array.c $ ./illegal_array 0 1 2 3 4 5 6 7 8 9 We change the *<=* to a *<* and the program runs as expected. ---- Leak Checking Example ************************** If we call malloc() in our program, and it exits without calling free(), then we have a memory leak. Compiling and running with gcc, will not inform us of this memory leak, however, compiling and running with dcc --leak-check will. The program below illustrates this feature. .. literalinclude:: linked_list.c :language: c :linenos: :caption: linked_list.c gcc ^^^^^^^^^^^ :: $ gcc -o linked_list linked_list.c $ ./linked_list 0->1->2->3->4->5->6->X dcc ^^^^^^^^^^^^ :: $ dcc -o linked_list linked_list.c $ ./linked_list 0->1->2->3->4->5->6->X dcc with leak check ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: $ dcc --leak-check -o linked_list linked_list.c $ ./linked_list 0->1->2->3->4->5->6->X $ Error: free not called for memory allocated with malloc in function create_node in linked_list.c at line 24. This output means that the memory allocated in the malloc call on line 24 in linked_list.c, was never freed. This line is: :: struct node *new = malloc(sizeof(struct node)); While it may be obvious in this program which malloc call is allocating memory which hasn't been freed (because there is only one line with a malloc call on it), these line numbers are helpful if you have many different mallocs on different lines of your program. .. moduleauthor:: Liz Willer :Date: 2020-01-15