In-Code Debugging


Clean code

동작을 도와주는 함수를 작성해서 코드를 모듈화합시다. 만약 반복되는 작업이 있다면 이 작업을 helper function으로 작성합니다 이 각각의 함수가 단독으로 잘 동작하도록 만들어 다시 디버깅하지 않도록 만듭시다.

다음 코드는 selection sort를 구현한 함수입니다.
void selection_sort(int *a, long len){
     for(long i = len-1; i > 0; --i){
         long max_index = i;
         for(long j = len-1; j >= 0; --j){
             if(a[max_index] < a[j]){
                  max_index = j;
             }
         }
         int temp = a[i];
         a[i] = a[max_index];
         a[max_index] = temp;
     }

}


위 코드에서도 버그를 발견할 수도 있지만(long j = i-1) 위의 함수를 아래의 3개로 refactor하면 어디서 문제가 발생했는지 좀 더 쉽게 찾을 수 있습니다.

long max_index(int *a, long start, long end);
void swap(int *a, long idx1, long idx2);
void selection_sort(int *a, long len);


이렇게 나누면 에러는 딱 하나의 함수에서만 발견됩니다.


사실 대부분의 시스템 코드는 심각할 정도로 읽기 싶지 않게 짜여져 있습니다. 하지만 디버깅을 위해서 이런 습관들 들이는 것이 좋습니다.


Asserts!

Assertion을 사용하면 코드가 특정 지점까지 잘 동작하는지 확인할 수 있습니다.
디버깅이 끝나면, 이 assertion을 없애는 것을 잊지 맙시다.
예를 들어 자료구조가 doubly linked list로 구성되어 있을 때
assert(node->size == node->next->prev->size)

위와 같은 assert를 넣는 것으로 다음의 노드가 현재 노드를 가리키는 포인터가 있다는 것을 확인할 수 있습니다. 또한 포인터가 가리키는 주소값이 NULL이 아닌 메모리의 주소값을 가리킨다는 것도 체크할 수 있습니다.

assert는 false일 때 중단됩니다. 즉 assert 안의 내용이 참임을 보장합니다.

NDEBUG 매크로를 사용해 모든 assertion을 사용하지 않게 할 수 있습니다. 디버깅이 끝나면 NDEBUG를 set하는 것을 잊지 맙시다.


memcopy를 사용할 때 overlap을 체크하기 위해 다음과 같은 assertion code를 사용할 수 있습니다.


assert(!(src < dest+n && dest < src+n)); //Checks overlap
memcpy(dest, src, n);



printfs

다른 모든 상황에서 실패한다면, print를 찍으십시오. 각각의 함수는 구현되어 있는 목적이 있습니다. 각각의 함수가 무엇을 하는지 무슨 값이 셋되어 있는지를 테스트해 보십시오.
race condition같은 경우, tsan을 통해 알아낼 수도 있지만, 각각의 쓰레드가 출력하는 데이터를 몇 번 출력해 보면 race condition을 확인할 수 있을 것입니다.


'Angrave System Programming > Learning C' 카테고리의 다른 글

Debugging(3)  (0) 2019.01.09
Debugging(2)  (0) 2019.01.09
Strings and Structs(2)  (0) 2019.01.09
Strings and Structs(1)  (0) 2019.01.09
Common Gotchas(3)  (0) 2019.01.09
Posted by 몰랑&봉봉
,