* 버그를 없애기 위해
- 찾아내기
- 문제점 진단
- 원치 않는 동작의 흔적 지우기
- 다른 곳에 영향을 끼치지 않았는지 확인
* bug를 찾아서 patch code를 만들 때는 work-around를 절대 조심해야 한다.
work-around는 돌아가만 가도록 하는 코드로, 보통 통신에서 명백하게 상대방의 잘못인데 상대방을 고치지 못하는 상황에서 어쩔 수 없이 사용하는 경우에만 허용해야 한다. 잘 기억했다가 근본 문제가 해결되면 지우는 것이 최종 목표이다.
* 결함을 찾을 때는 모든 것을 의심해야 한다. 경험이 많아질수록 의심할 수 있는 범위가 넓어진다.
* Timing : 의외로 문제를 많이 일으키는 녀석이다.
프로그램은 논리적인 절차대로만 움직일 것 같지만 사실은 시간이 중요한 역할을 한다. 특히 통신에 있어써는 더욱 그렇다.
대학생 때 카메라로 사진을 저장하여 얼굴을 인식하고 특징점을 잡는 프로젝트를 했는데 일련의 과정을 수동으로 버튼을 눌러가면서 진행하면 잘 돌아가는데 한번에 자동으로 돌아가게 하면 crash가 발생했다. 원인은 이미지가 저장되는데 시간이 걸리는 것 때문에 read를 했을 때 일부만 읽어 처리하여 저장해야 할 공간보다 원본의 필셀이 부족한 것이었다.
10년이 넘는 시간이 지나 실무에서도 비슷한 일을 겪었다. 통신 중에 이미지를 요청하고, 저장하고, 화면에 뿌리는 일련의 과정에서 각 과정이 끝난 것을 확인하고 다음 단계로 넘어갔는데도 이미지가 잘리는 현상이 발생했다.
이미지는 고유한 handle이 있어 이를 이용해 요청하고, 이를 이용해 저장하므로 중복이 되지도 않는다. 라고 생각했지만 그것이 문제를 일으켰다. store에서 설치한 app들이 handle을 가지고 이미지를 요청하게 되는데 같은 handle에 대해 두 번씩 요청하는 app이 있었고, 그것이 문제를 만들었다. 이미지 저장이 끝나고 read를 하는 도중에 다시 write를 하고 있었던 것이다.
* 디버깅에는 정답이 없다. 애초에 있지 말아야 할 오류를 잡아내는 일이기 때문이다. 그래서 많은 사람들이 이에 조금이라도 도움을 주기위해 오류가 발생하지 않도록 하는 방법론을 만들고 Tool도 만들었다. 그런데 이미 발생한 오류를 잡는 방법은 없을까? 과학자들이 과학적인 현상에 접근하는 방식은 4단계가 있다고 한다.
1. 현상을 관찰
2. 가설 성립
3. 가설을 이용한 결과 예측
4. 예상이 맞는지 실험
* Bug의 발견
여기서 말하는 Bug란 잘못된 동작 즉, 고장이란 말이다. 물리적인 고장이 아니라 원하는대로 동작하지 않는 증상을 말한다. 고장을 식별하는 것은 개발자가 먼저 거르고, 품질 관리팀에서 다시 검증하고 나서도 마지막으로 최종 사용자에 의해 발견되기도 한다. 일반적으로는 품질 관리팀에서 발견하는 이슈를 많이 처리한다.
* 식별
가장 먼저 해야할 일은 이 증상이 정말 Bug인지 확인하는 과정이다.설계된 대로 동작하는데 내 마음에 들지 않는다고 Bug라고 할 수는 없으니 말이다. 이를 위해 예상 동작과 bug로 인식한 동작을 함께 기록하여 보고한다.
예상 동작 : A->B->C
실제(발견) 동작 : A->B->A
*Logging
문제가 발견되면 분석과 해결을 위해 log를 엔지니어에게 넘기게 되는데 이 부분이 관건이다. 단순 프로그램은 printf를 파일로 저장한 정도면 충분한데 Embedded 통신에서는 log의 종류가 너무 다양하다.
Bluetooth를 예로 들어보자.
Over The Air log : RF 통신을 하기 때문에 무선을 logging하는 장비들이 있다. 이 기록이 통신상의 문제, 특히 상대 장비(peer device)의 문제를 파악하는데 필요하다.
HCI sniff log : Host AP와 BT chip(controller)사이에는 보통 UART로 통신하는데 이 부분에 대한 log도 필요하다. UART flow control pin을 포함하여 잡는데 packet을 분석하면서 잡는 툴 들이 있다. 보통 Air log를 잡는 sniffer가 이를 함께 지원하는 경우가 많다.
Snoop log : HCI sniff log와 마찬가지로 HCI로 주고 받는 packet을 파일로 저장한 log다. 실제 통신은 아니고 AP에서 주고 받는 것을 저장하기 때문에 참고용이다. Hardware를 수정할 필요가 없이 파일로 저장만 하면 되기 때문에 HCI sniff log대신 쉽게 사용한다. 다만 Host와 Controller간 UART 문제가 의심되면 HCI log와 snoop을 비교해봐야 한다.
Software log : software도 stack부분과 application이 구분되어 보고자하는 위치에 따라 다르게 저장해야 한다. 심지어 logging level에 따라 저장되는 정도가 달라 이슈에 따라 level을 변경하면서 저장해야 한다.
기타 : 상황에 따라 system(kernel) log, PCM log등이 요구되기도 하고, Controller 내부에 발생한 이슈를 분석하기 위해 별도의 GPIO를 추가하여 분석하기도 한다.
*재현
문제를 다시 일으키는 것은 상당히 중요하다. 애초에 test engineer가 확인할 때 이렇게하면 오류가 발생한다는 메뉴얼이 있는 상태가 아니기 때문에 bug가 발견되는 것은 사고처럼 갑자기 찾아온다. 그래서 오류를 찾기 전부터 이미 logging을 시작한다. log의 종류가 너무 많아 다 잡을 수는 없다. 그래서 초기 분석 후 추가로 필요한 log를 계속 취득해야 하는데 재현이 잘 되는 경로를 찾는 것이 빠른 해결의 핵심이다.
Test engineer의 실력은 중요한 문제를 찾아내는 것보다 재현이 잘 되는 경로를 찾는 것이 말해준다. 실력 있는 Test engineer는 어떻게 하면 재현이 되는지를 찾아낼 수 있다. 하지만 재현 경로를 제대로 찾는 것은 문제를 거의 해결하는 것과 비슷한 수준으로 어렵다. 때로는 재현 자체가 어려운 문제도 있다. 상당한 고산지대에 올라가면 발열이 심해지는 TV라던가, 일주일 이상 충전하면서 연결상태를 유지하면 연결이 끊어지는 리모컨이라던가, 특정지역에서 광역버스가 지나갈 때만 음질에 문제가 생기는 헤드셋이라던가 하는 것들은 사무실에서 쉽게 재현하기가 어렵다.
통신은 상호간에 주고 받는 동작이기 때문에 상대방의 동작이 문제를 일으켰다고 생각이 되면 이상한 동작을 하는 제품을 확인하고 그와 유사한 동작을 하는 프로그램을 만들어 재현을 해야 할 때도 있다.
그래서 가능하면 이슈가 발견될 때 재현 경로와 재현 빈도를 물어본다. 이 때, 문제에 대한 이해가 부족한 많은 Test Engineer는 그냥 일반적인 동작을 하다가 한번 발견되었다고 대답을 한다.
*patch
bug의 위치를 찾아냈다면 수정하는 것은 쉬운 경우가 많다. 그러나 주의할 것들이 당현이 있다.
- 전역변수 : 아무데나 선언하고 아무데서나 사용하는 전역 변수는 multi thread/task 환경에서 많은 문제를 일으킨다. 사용하지 못하도록 구분해놓은 코드 파일에 굳이 extern까지 써가며 전역변수로 patch를 하는 사람을 실제로 목격하기도 한다. 물론 이런 사람들에게 mutex같은 critical section의 개념은 찾아보기 어렵다. 통신에서는 자주 timing이 문제를 일으키는데 전역 변수로 오류를 쉽게 수정하는 것은 내일의 일은 절대 생각하지 않겠다는 의미다.
- Work-around : 이론적으로 맞지 않지만 우선 해결하기위해 넣는 코드다. 통신에서 상대방이 잘못된 행동을 하는데 그 상대방이 내 이야기를 들어주지 않을 만큼 크고 사용자가 많은 회사라면 우리가 알아서 기어야 한다. 이러한 코드는 어쩔 수 없이 들어가지만 다른 의미로 work-around를 사용하는 사람이 있다. 원인을 모르겠으니, 아니면 제대로 해결이 어려우니 당장 땜질만 해서 넘어가자는 식으로 제대로 된 patch가 아니라 work-around를 집어넣는 사람이 있다. 한명의 엔지니어가 모든 일을 하는 기업이 아닌 이상 같은 코드를 바라보게 되는데 모두가 공유하는 우물에 쓰레기를 버리는 짓이다. 이러한 코드는 나중에 side-effect를 유발할 가능성이 크다.
- 손이 눈보다 빠르다 : 제대로 된 분석 없이 문제가 되는 부근의 코드를 이리저리 마구잡이로 고치며 어떤 변화가 있는지 때려 맞추는 식으로 debug를 시작하는 사람이 있다. 이런 식의 방식은 실력을 키우는데 크게 도움이 되지 않고, 어쩌다가 문제가 나오지 않게 되면 더이상 진행하지 않고 그자리에 멈추게 될 가능성이 크다. 제대로 된 원인을 분석하지 않고 그 부분만 막는다면 나중에 다른 곳에서 터질 가능성이 있다.
요즘은 static/dynamic tool들이 많은 오류를 사전에 찾아주어 프로그램을 만들 당시부터 오류를 상당히 많이 막아준다. 하지만 Android처럼 이미 상당히 많은 양의 코드가 존재하는 코드를 tool을 이용해서 발견된 코드를 모조리 수정하려고 들면 정말이지 개발을 할 시간이 아예 없게 된다. tool은 존재하지만 적당히 모든 것을 보는 것은 아니고 중요한 일부만 확인하는 적당한 타협이 필요할 때도 있다. 나중에는 논리적으로 문제가 없는 선에서 자동으로 수정까지 해주겠지?
'Programming > 좋은습관들이기' 카테고리의 다른 글
Debugging : 무언가 잘못 돌아갈 때 해야 할 일 (0) | 2024.06.23 |
---|---|
[코드멍키] 생각해 봅시다 (0) | 2022.01.08 |
프로그래머의 종류 (0) | 2022.01.03 |
설계하기 (0) | 2022.01.02 |
최적화 (0) | 2022.01.01 |
Build 관점에서의 language 구분 (0) | 2021.12.31 |
Comment 작성 요령 (0) | 2021.12.30 |
.vimrc option (0) | 2011.07.22 |
Artistic Style : SourceInsight (0) | 2011.04.28 |
툴을 사용하자 (0) | 2008.07.08 |