졸음 버그

글에 앞서 프로그래머를 졸린 상태에서 일하도록 하면 안된다고 강력히 주장합니다.

제가 요즘 좀 많이 바쁩니다.

Todo list로 적어봤더니 노트가 꽉차더군요-_-

어제 밤에는(정확히는 오늘 6시까지) 과제기획서를 위한 자료 준비를 하고 6시부터 아르바이트로 하고 있는 프로그램을 코딩했습니다. 코딩 중에는 정말 난리도 아니었죠. 졸린 눈 비비고 깜빡 졸았다가 다시 타이핑하고, 커피를 냅다 위에다 붓고.. 이게 수요일까지 완성되어야 하기 때문에 정말 바쁩니다. 게다가 전 다음주 일요일부터 시험이라구요! 이번주 일요일까지는 과제 기획서, 기말 프로젝트 보고서를 제출해야 하고, 화요일까지 프로그래밍 과제도 있고, 수요일까지 프로그래밍 및 UML 보고서를 제출해야 하고, 같은 날에 Vigenere 암호 해독 프로그램을 작성해야 하며 시험기간이 끝나자마자 알고리즘 22개에 대한 설명과 최선, 최악, 평균에 대한 예제를 만들어서 손으로 풀어서 제출해야 하고, 프로그래밍 언어론에서 프로젝트를 진행할 언어를 선정하는 게시글을 올려야 하며, 토론글을 작성해야만 합니다.(그렇지만 더 나쁜 건 이게 다가 아니라는 겁니다. 더 많이 있어요-_-) 잠을 줄이지 않고는 할 수가 없겠더군요.

그래서 잠을 줄였습니다.

그 결과로 아침에 다음과 같은 코드를 작성했답니다.

   for(i = 0; 1 < 10; i++)    {
       ar >> sData.inclineStart    [i];
       ar >> sData.inclineEnd        [i];
       ar >> sData.inclinePercent    [i];
   }

뭐가 잘못됐는지 보이시나요? 저는 제가 평생 이런 코드를 짜지 않을 거라고 생각했습니다. 저런 코드를 짜는 프로그래머는 “죽어야 된다”고 생각했죠-_-;;; “어떤 바보멍청이가 저런 코드를 짜?” 라고 생각했죠. 그 바보 멍청이가 오늘 제가 될 줄은 꿈에도 몰랐답니다.

이 것 말고도 몇가지 사고(!)들을 일으키는 코드를 아침에 수없이 양산해냈죠; 저장을 두 번 한다던지.. 배열의 index를 다른 변수의 index와 헷갈린다던지.. 이게 저 혼자 짜는 프로그램이니 다행이지 협업되서 돌아가는 프로그램이었다면 어땠을지 정말…

음주 코딩, 졸음 코딩.. 정말 없어야 합니다. 음주 코딩이나 졸음 코딩을 해야 할 정도로 프로그래머를 괴롭히지(!) 마세요. 두배, 세배, 열배, 백배가 되어 다시 돌아옵니다.(대부분 프로그래머에게 다시 돌아오게 되어 프로그래머를 더 힘들게 하긴 합니다만)

근데 지금도 정말 졸립고 힘드네요.

MIPS 시뮬레이터의 버그

학교에서 컴퓨터구조 수업을 듣는 중입니다.
IA-32(교수님 표현을 빌리자면 걸레-_-) 구조가 아닌 간단한 MIPS 아키텍처에 대해 배우고 있는데 MIPS Instruction set 을 이용해서 간단한 ASM 프로그래밍을 과제로 내주셨기에 어제 열심히 했더랍니다. 시뮬레이터로는 PCSpim 이라고 하는 윈도우즈 기반(이 것부터 잘못일지도 몰라요-_-) 시뮬레이터를 썼습니다. SPIM 은 Unix/Linux 기반의 콘솔 환경에서 사용할 수 있는 도구입니다. PCSpim은 그를 윈도우즈의 GUI 환경으로 만든거구요.

그런데! 아무리해도 원하는대로 답이 안나오는 겁니다!

답이 안나오면 가장 먼저 의심하는건 접니다-_-;; PC는 거짓말을 하지 않잖아요?
이거 뭔가 이상하다고! 컴파일러가 잘못되었다고! 외쳐봐야 나중에 보면 결국 자기 탓이기 마련입니다.

그런데 어제는 아니더군요.(컴파일러는 아니니 그럴 수 있는 건지도 모르겠지만요~ 😉 )
코드를 열심히 들여다보고, 내 능력이 이 거 밖에 안되나~ 한숨도 쉬어보고, 다시 아무리 코드를 들여다보면서 돌려봐도 안되는 겁니다. 난감하더군요.
결국 차례차례 나눠서 한 스텝씩 진행해보았습니다. 자세히 한스텝씩 진행하며 살펴보니 이 PCSpim 이란 놈이 자기 멋대로 Stack Pointer Register 값을 바꿔버리고 있더군요. 그래서 $SP 의 값을 가지고 리턴하는 부분에서 엉뚱한 곳으로 리턴하면서 Exception을 툭툭 뱉어내고 있는 것이었습니다.

사용자 삽입 이미지
jump전의 $SP값

사용자 삽입 이미지
$SP값이 제멋대로 바뀌었다!

그래서 바로 리눅스로 재부팅하고! 우분투의 소스리스트에 있는 SPIM 을 설치하려고 했으나 제대로 동작하지 않길래 소스를 받아다가 소스 컴파일하고 설치를 했죠.

바로 결과가 나오더군요-_-… 아까운 제 3시간은 그렇게 흘러갔습니다. 버젼이 7.3이나 되는 놈이 이럴 줄은 몰랐답니다ㅠㅠ

추가 : 다른 컴퓨터에서는 제대로 돌아가길래 PCSpim을 재설치하고 다시 해보았는데도 안됩니다. 그래서 뭔가 이상하다 생각하고는 Simulator 메뉴의 Settings… 에 있는 옵션들을 건드려보았는데 Delayed Branches 와 Delayed Load를 체크상태로 두면 (제 노트북에서만) 제대로 동작하지 않습니다. 이게 어떤 역할을 하길래 $SP 값을 마구 바꾸는 건지 모르겠네요. 게시판에 질문을 올렸는데 답변이 오면 또 추가하도록 하겠습니다.

변수 초기화의 중요성

변수를 선언하고 정의할 때 초기값을 넣어 초기화한다.

기본 중의 기본입니다. 이 말을 왜 하는가 하면-_-;;

아르바이트로 VC 6로 프로그램을 하나 작성중인데 DEBUG 모드에서는 없던 문제들이 RELEASE 모드에서 빌드한 후에 마구 터져나오더군요;
역시나 쓰기전에 초기화하지 않은 변수들때문이었죠.

한가지는 윈도우 크기를 콤보박스의 선택에 따라 바꾸는 기능을 넣고 싶어서 원래 창의 크기를 m_Rect 에 기억시켜뒀다가 선택에 따라 m_Rect.Height() <-> m_Rect.Height() + 30 으로 창의 높이를 변경하려고 했습니다. 헤더에 CRect m_Rect; 를 선언하고 다음과 같이 작성했죠.

   CRect rect;
   GetWindowRect(&rect);
   if(m_Rect.Height() == 0)    {
       m_Rect = rect;
   }

이게 제대로 동작하려면 당연히 초기에 m_Rect.Height() 가 0이어야합니다.(!)
생성자에 넣어야지. 라고 생각하고 까먹었던거죠(…) 이게 없던 결과로 m_Rect.top 과 m_Rect.bottom 에는 -8203…어쩌고 하는 값이 들어가서 윈도우가 안보이더군요. 전 프로그램이 뻗은 줄 알았어요-_- DoModal() 로 띄워둔 창은 안보이고 아래 윈도우에 Focus() 가 가지 않고 모든 입력을 거부하고 있으니 어디서 무한루프를 도나? 하고 생각했던거죠.
이 문제는 생성자에 다음을 넣음으로써 간단히 해결됐습니다-_-

   m_Rect.top        = 0;
   m_Rect.bottom    = 0;

또 다른 문제는 같은 역할을 하는 double형 값을 입력받는 Edit 창 10개를 만든 후 개수에 따라 Visible 한 Edit의 개수를 변경해서 값을 입력받는 것이었습니다. 먼저 헤더에

   double    m_dDip[10];
   double    m_dDipDirection[10];

과 같이 선언하고, DoDataExchange 변수에

   DDX_Text(pDX, IDC_EDIT_JOINT_DIP01, m_dDip[0]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP02, m_dDip[1]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP03, m_dDip[2]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP04, m_dDip[3]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP05, m_dDip[4]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP06, m_dDip[5]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP07, m_dDip[6]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP08, m_dDip[7]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP09, m_dDip[8]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP10, m_dDip[9]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_01, m_dDipDirection[0]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_02, m_dDipDirection[1]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_03, m_dDipDirection[2]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_04, m_dDipDirection[3]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_05, m_dDipDirection[4]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_06, m_dDipDirection[5]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_07, m_dDipDirection[6]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_08, m_dDipDirection[7]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_09, m_dDipDirection[8]);
   DDX_Text(pDX, IDC_EDIT_JOINT_DIP_DIRECTION_10, m_dDipDirection[9]);

와 같은 코드를 작성했더랍니다.

근데 다이얼로그를 띄우면 “숫자를 입력하세요” 란 메시지가 나타나면서 IDOK를 거부하는 겁니다-_- 역시나 멤버 변수의 값들의 초기화 문제 때문이었습니다.
이문제도 생성자에 다음을 넣어서 간단히 해결되었습니다-_-

   int i = 0;
   for(i = 0; i < 10; i++)    {
       m_dDip[i]            = 0;
       m_dDipDirection[i]    = 0;
   }

아고.. 암튼 삽질의 연속입니다. 역시 기본을 충실히 지키지 않으면 언젠가 댓가가 따르는 법입니다. 전 그 댓가로 3시간 자고 일어나서 전화받고 깨서 버그 잡고 있습니다. ㅡㅜ