본문 바로가기
기타 정리

CallStack을 찍어보자.

by 날쑤 2015. 8. 7.

Intro

  커스텀 메모리 관리자에서는 일반적으로 디버깅을 용이하게 하기 위해 메모리 할당이 일어난 위치 정보를 메모리 헤더에 기록한다. 이는 예전 강좌 정리글에서도 한번 다뤘었다. 이러한 위치 정보 기록은 메모리 관련 뿐만 아니라 여러 곳에서 유용하게 쓰일 수 있다.

  그런데 어떤 상황에서는 단순히 그 상황이 일어난 위치정보만으로는 문제 해결이 어려울 수 있다. 특히 문제가 되는 지점으로 진입할 수 있는 경로가 여러 개일 경우는 이것이 어떤 경로에서 발생한 문제인지 확인하기가 힘들다. 이때 만약 문제 시점의 콜스택을 확인할 수 있다면 이는 골치아픈 디버깅을 하는데 있어서 큰 도움이 될 것이다. Windows 프로그래밍에서는 이를 가능하게 하는 함수들을 제공하고 있는데, 이를 사용해서 콜 스택을 찍어보도록 하자.

1. CallStack 상의 symbol 주소 찍기

  일단 CallStack 자체를 찍어야한다. CallStack을 찍기 위해서는 CaptureStackBackTrace 함수를 이용한다. 그리고 함수의 결과값을 저장하기 위해 CallStack 클래스를 정의하자.

  위의 CallStack 객체는 생성되는 시점의 CallStack을 저장한다. 그런데 저장은 address_ 배열에 주소값으로 저장된다. 이것만으로는 의미있는 정보를 얻기 힘들다. 이 주소값을 바탕으로 pdb로부터 심볼 정보를 얻어내야한다. 그러므로 주소값이 주어졌을 때 pdb에서 심볼 정보를 lookup하는 기능을 만들자.

2. 심볼 정보 lookup 기능

  심볼 정보는 SYMBOL_INFO 객체를 헤더로 가지는 메모리에 저장된다. SYMBOL_INFO 객체를 헤더에 저장하는 방식은 여러가지가 있겠지만, 여기서는 상속을 이용해서 심볼 정보를 저장할 메모리 객체를 정의한다.

  심볼 정보를 저장할 객체도 정의했으니 본격적으로 심볼 정보를 lookup 해서 저장하는 일만 남았다. 사실상 이 부분이 구현의 핵심이라고도 할 수 있겠다. 여기서는 SymbolLookup이라는 이름으로 필요한 사전작업 및 기능을 하나의 클래스로 묶었다.

  여기까지 오면 interface 정의 및 심볼 정보를 얻기위한 사전 작업은 모두 끝났다. 이제 readable한 std::string으로 심볼 정보를 반환하는 GetSymbolString 멤버 함수만 구현하면 끝난다. 남은 작업은 모두 이 함수 내에서 수행된다.

  이제 모든 준비가 끝났다. CallStack이 제대로 찍히는지 확인해보자.

결과 확인

  전체 프로그램의 수행결과는 아래와 같다.

  프로세스가 생성되는 시점부터 CallStack 객체의 생성자까지 문제없이 출력이 된다. 몇줄 안되는 코드이긴 하지만 알아두면 차후에 요긴하게 써먹을 수 있지 않을까라는 생각이 든다.

'기타 정리' 카테고리의 다른 글

boost의 static_gcd / static_lcm 흉내내기  (0) 2015.10.28