Trace. Trace? Trace!

0. Trace.
원래 개발자란 족속들은 게으르고 게으르고 또 게으른 관계로 족적을 잘 남기지도 않고, 문서는 잘 쓰지도 않으며, 재미있는 개발이 끝나고 나면 한없이 늘어지기 마련이다. 이거야 뭐. 인간이란 원래 그런 족속이니까.

1. Trace?
그럼에도 불구하고, 개발자란 족속들은 언제나 족적을 남기기 마련이다. 코드에 남기기도하고, 주석에 남기기도 하고, 문서를 쓰기도 하고, 무려 코드를 저장할때마다 커밋로그라는 것도 남긴다. 사실 지금 직장에서 Subversion + 커밋로그강제스크립트를 도입한지도 어언 6년반정도 되었다. 처음에 다들 싫다고 징징거리던 것을 억지로 우겨넣어서 지금의 상태인 것인데, 커밋로그를 강제하니 뭔가 족적을 하나씩 -그나마 쓸만하게- 남기긴 한다. 다들 커밋로그가 좋다는 걸 나중에야 알게된 것이겠지.

2. Trace!
그래서 든 생각이 이런거다. ‘왜 개발자만 족적을 남기는가?’, ‘요즘같이 빠른 세상에 정중하게 하나하나 맞추어 문서를 쓰기란 뭐같이 힘든 일이니, 빠르게 족적이라도 남기면 좋지 않겠는가?’

하지만, 연구소에서나 가능한 일을 전사에 강제하기란 그리 쉬운일은 아니다. 하아.

디버깅: 누가 나의 this를 옮겼을까? #1. 아이디어.

C++ 컴파일러들은 아주아주 훌륭합니다. 최적화를 켜면 이리저리 쿵짝쿵짝 뭔가 대단해보이는걸 해치워서 코드를 빠르게 만들죠. 요런류의 최적화중 중요한 한가지 기법이 특정 변수를 register를 사용하게 만드는 겁니다. 넵. 대단합니다. 멋지죠. DRAM보다는 Cache가 빠르고 Cache보다는 Register가 빠르니까요. 빠른게 최고인겁니다.

하지만, 디버깅 과정에서는 재앙입니다. 저런 최적화를 수행하게 되면.. 변수가 날아갑니다. 특히 this포인터는 무용지물이 됩니다. MSVC의 경우, this포인터는 CX레지스터를 이용하는데.. 최적화를 수행할 경우 CX레지스터를 범용 연산 레지스터로 씁니다. 즉.. CX레지스터를 다른 용도로 쓰게 되고.. this포인터는 이상한 값을 남발하죠. (보통 0인 경우가 많습니다.) 재현이 가능한 버그라면, 최적화를 끄고 재현해서 문제를 해결할 수 있겠지만, Post-mortem디버깅(크래쉬덤프를 이용한 디버깅)이나 타이밍 문제로 버그가 발생하는 동시성 문제라면 최적화를 끌수도 없습니다. 전자는 끄는게 불가능하고, 후자는 경험상 단언컨데 최적화 끄면 문제가 안생길 확률이 은근히 높습니다. OTL

가장 근본적인 방법은 disassemble된 코드를 읽어서 this포인터를 찾는 것이지만.. 사실상 힘들죠. 그 값이 유지되고 있다는 보장도 없구요. 결국은 포기하기 마련입니다. 아니면, 소설을 쓰거나요. 음… 일단 상황을 정리해보면 아래 3가지로 정리가 될겁니다.

  1. 최적화를 끄고 재현이 가능한 상황.
  2. 최적화를 끄면 재현이 안되는 상황.
  3. 재현이 불가능한 상황(Post-mortem)

1번의 경우에는 앞에서 언급한대로 그냥 최적화를 끄고 디버깅을 하면 됩니다. 문제는 2번과 3번인데, 2번은 코드를 살짝 수정해서 문제가 되는 부분의 this pointer를 전역변수로 저장해두면 됩니다. 의외로 간단하지만 쓸만하죠. 마지막, 3번이 골아픕니다.

Post-mortem 디버깅이란 프로세스가 비정상종료될때 남기는 정보를 이용해 버그를 잡아내는 방법을 의미합니다. 보통 Win32플랫폼에서는 이를 위해 MS에서 제공하는 DBGHelp라이브러리를 이용해 미니덤프파일을 남기게 됩니다. 물론, Dr.Watson을 이용해도 되지요.

미니덤프를 이용해 프로세스가 비정상종료될때의 위치를 파악한다고 해도, 앞에서 이야기한 것처럼 최적화된 바이너리라면, 문제를 찾아내기 어려울 경우가 발생합니다. 이때 사용가능한 기법이 메모리검색입니다.

네? 메모리 검색이라구요??

네. 메모리 검색입니다. 우리에게 필요한 것은 잃어버린 this 포인터이고, 결국은 메모리안에 존재합니다. 그러므로, 어떻게든 찾아내면 되는 것이죠! 이제 어떻게 찾아내는지가 관건일텐데.. 바람직한 C++프로그래머라면, 아마도 소멸자를 가상함수로 선언해두었을 가능성이 높습니다. 사실 이거 하나면 충분하죠. 무슨 이야기냐구요?

C++은 다형성을 구현하기 위해서, 가상함수테이블을 사용합니다. 그리고, 각 인스턴스들은 가상함수테이블에 대한 포인터를 들고 있습니다. 즉, 인스턴스의 메모리 레이아웃에 포인터변수가 선언되어 있는 것이지요. (_vftable!) 잃어버린 this 포인터가 가리키는 메모리, 즉 해당 클래스의 인스턴스가 차지하고 있는 공간에는 분명히 가상함수테이블에 대한 주소가 적혀있을겁니다. 슬슬 느낌이 옵니다. ㅎㅎㅎ

자. 이제 _vftable의 주소와 이 주소를 갖고있는 메모리를 찾으면 됩니다. :)

To be continued.

구인-구직: 최근의 면접(관)후기.

일하면서 동료들과 농담삼아 하던 이야기가 있다.

버텨야해! 몸값은 오를꺼야!

IT를 밥줄로 먹고산지도 햇수로 12년째다. 정규직 붙밖이로 일을 시작한 것이 2004년이니 흔히 말하는 경력으로 치자면 벌써 8년차 개발자인가. 이렇게 일하면서 느끼는 사실이지만, 갈수록 같이 일할 사람을 구하는 것이 힘들다. 흔히 말하는 ‘스펙’은 신규 인력의 가능성을 간접적으로 보는데는 유용하지만, 이 스펙을 쌓느라 기본적인 지식들이나 경험이 부족한 인력들을 양산하는 주된 원인이 된다. 특히 프로그래밍은 재미를 붙이는 사람들이 필요한데, 학점을 올리고 영어점수를 신경써야하는 스펙공장에서 과연 재미를 붙일 수 있을까. 한줄의 코드가 자신의 재미가 아니라 학점의 도구가 되어버렸는데.

최근 면접을 보면서 느끼는 점이 바로 스펙의 폐해다. 이미 스펙향상의 도구가 되어버린 대학교육에서 면접자들이 배워오는 것은 점수와 이력서에 적힌 몇줄의 경력사항들. 전공에서 배웠을 가장 기초적인 것들을 물어보았을때 우물쭈물하고 가능성을 보기위해 가벼운 문제들을 내었을때 당황해하는 모습들을 보면서 참 씁쓸할 따름이었다. 그나마 순발력을 보여주는 면접자들은 다행스럽지만, 그도 보여주지 못하는 면접은 솔직히 시간낭비라는 생각이 들 정도다. 사실 여유가 많다면 왕창 뽑아서 인턴을 돌리며 테스트를 하는 것도 좋겠지만, 중소기업에서 이게 쉬운 일인가. 인턴을 몇번 받아서 일을 해보았지만, 인턴을 관리하고 일을 시키는 것도 상당한 부하를 발생시킨다. 여유가 있으면 모르겠지만, 이럴 여유가 되는 회사가 과연 얼마나 될까?

뭐. 몸값이 오르겠지. 몸은 피곤해지겠지만. 후아.

클래스는 영원하다: 범용 컴퓨팅 장치의 크기.

“클래스는 영원하다.”

빌 샹클리라는 축구감독의 발언에서 유래한 최근의 이 유행어는 스마트폰, 7인치 탭, 10인치 패드의 ‘클래스’에도 정확하게 적용된다. 이 ‘클래스’는 성능같은 geek한 속성에 의해 좌지우지 되는 것이 아니라, 단 한가지의 속성에서 결정된다. 그건 바로.

크기

크기! 크기! 크기! 무게나 성능, 두께같은 다른 속성은 부차적일 뿐이고, 가장 중요한 것은 크기. 화면 크기다. 화면 크기는 상당히 많은 것을 내포하고 있는데, 크기는 무게나 두께를 동반하고, 이렇게 도출된 물리적 스펙은 이동성을 결정짓는다. 이 이동성은 배터리의 성능과 연결되어, 탑재가능한 CPU를 결정짓고 성능을 도출한다.

크기가 가장 중요한 요인인 또다른 이유는 인간의 시력에 있다. 인간이 볼 수 있는 글자의 최소 크기는 정해져 있다. 개인별로 차이는 있겠지만, 최소 10pt이상은 되어야 편안하게 읽는 것을 보통이라고 가정할때, 장치별 화면의 크기는 해당 장치에서 한번에 볼 수 있는 정보의 양이고, 그 정보의 양은 장치의 사용성을 결정짓기 마련이다.

물론, 역으로 생각해보면, 사용성에 맞추어 그 크기를 결정지었다고 할 수 있다. 현재 눈앞에 놓여진 클래스는 ‘화면 크기’다. 정보를 ‘읽는’다는 측면에서 접근해보면 좀 간단할 것 같다. 흥미롭게도 책장의 책을 유심히 살펴보면 비슷한 분류의 책들은 그 크기도 비슷하다는 걸 알 수 있다. 컴퓨터 관련 서적들의 크기는 다 비슷하고, 소설책들의 크기 역시 다 비슷하다. 소설책을 A4로 내지는 않으며, 레퍼런스를 문고판으로 내지는 않는다. 컨텐츠가 요구하는 사용성에 맞게 크기가 결정된 것이다. 논리의 앞뒤가 바뀐다고 해도 결국 크기가 중요하다는 사실은 변하지 않는다. 실제로 아이패드가 처음 나왔을때 덩치큰 아이팟 터치라고 비웃음을 샀지만 (필자도 그 비웃음에 동참했었다.) 실제로 써보니 전혀 다른 경험이었다.

현재 가장 대중적인 클래스인 4인치급의 스마트폰, 7인치급의 탭, 10인치급의 패드, 12인치급의 노트북은 각각의 클래스에서 경쟁하는 제품이지 서로 경쟁하는 제품이 아니다. 즉, 삼성이 내놓은 갤럭시탭은 아이패드의 경쟁자가 아니라, 7인치탭의 시작점에 불과하다.

새로운 개념의 제품이 등장하고 각 클래스에서 패러다임이 바뀌는 현상은 4인치급에서 적나라하게 확인할 수 있다. 언젠가부터 PDA라는 용어가 사라졌으며, 스마트폰이 대세다. Palm과 WinCE로 대표되던 PDA들이 아이폰을 앞세우고 안드로이드가 뒷받침하는 스마트폰에게 처참하게 패배한 것이다. 이런 전례로 비추어 보건데 예상되는 경쟁관계는 7인치급의 네비게이션/PMP와 탭의 경쟁, 그리고 10인치급의 넷북과 패드의 경쟁이 될 것이다. 4인치급의 전쟁을 돌이켜보건데, 획기적이고 파급력있는 제품이 뚫고, 유사한 다종의 제품들이 기반을 다지는 형태의 상황이 될 가능성이 높다. 10인치급은 애플의 iPad라는 에이스가 존재하지만, 7인치급의 갤럭시탭이 그 에이스의 역할을 담당할 수 있을지는 의문이다. 10인치급의 넷북은 iPad가 열어제낀 패드들에게 밀려나갈 가능성이 높으며, 7인치급의 탭은 네비게이션/PMP들이 탭의 기능을 흡수하면서 통합될 가능성이 높다고 본다.

4인치, 7인치, 10인치 등의 화면크기는 손에 “들고” 사용해야하는 상황의 모바일 범용 컴퓨팅 장치에서는 결정적인 factor이며, 당분간 이 클래스는 변동치 않으리라고 본다. 축구에서처럼 클래스는 영원하다.

ps. 적어놓고 보니 당연한 이야기를 어지럽게 풀어버렸다. 이런 산만함이라니. orz

디버깅: 마음가짐.

전 사실, 대학에서 공학을 전공했습니다. 그것도, 산업공학이란 시스템을 다루는 공학을 전공했지요. 대학시절이 제게 남긴 가장 큰 가르침은 시스템에 대한 정의와 공학적 문제 해결 방법입니다. 시스템에 대한 정의는 언젠가 설계와 관련한 글을 쓸때 써먹게 될 것이고, 오늘 이야기할 디버깅은 공학적 문제 해결 방법을 써먹게 되겠네요.

일을 하면서 느끼는 것은 디버깅을 어렵게 느끼는 사람들이 많다는 점입니다. 어렵게 느끼는 사람들 혹은 어렵게 느끼는 상황을 곰곰히 생각해보면, 어려운 이유는 단 하나입니다. 막막하다는 것이지요. 보통, 막막함에 당황하고, 당황하니 더 막막한 악순환의 고리로 빠져드는 경우가 많습니다. 일단은 침착해야 합니다.

차분하게 공학적 문제 해결 방법을 따라서 생각해보는게 좋습니다.

  1. 문제를 탐색한다.
  2. 문제를 정의한다.
  3. 자료를 수집한다.
  4. 자료를 분석한다.
  5. 대안을 생성한다.
  6. 대안을 평가한다.
  7. 대안을 선정한다.
  8. 대안을 적용한다.
  9. 1번으로 돌아간다.


네. 사실 ‘당연한거 아냐?!’ 라고 생각하기 쉬운 당연하고 자명한 이야기입니다만, 당황하게 되면 의외로 잊기 쉬운 기본적인 것들입니다. 타석에 들어선 야구선수가 공을 끝까지 보고 스윙을 해야하듯이 디버깅도 항상 차분하게 공학적인 자세로 접근해야 하는거죠. 아무리 시스템이 복잡하다해도, 아무리 답이 안보이는 것 같은 문제라 할지라도, 차분하게 접근하면 대부분의 버그들은 찾아낼 수 있습니다. 차분한 마음가짐이 있다면, 비로소 여러 테크닉들을 적용할 수 있게 되니까요. 어려운 버그는 없습니다. 다만 복잡해서 막막한 버그일 뿐이지요. 모든 버그는 그냥 버그일 뿐입니다. :)