티스토리 뷰

개발/일상

gdb 기초를 알아보자

clucle 2021. 8. 5. 00:34

목차

1. 코어 남기기

2. 에러 위치 찾기

3. 클래스 맴버 변수 출력하기

 

1. 코어 남기기

프로그램이 실행되는 도중 얘기치 못하게 종료되는 상황이 오면 코어를 남길 수 있다. 그러나 일반적으로는 코어파일이 생성 되지 않는데, 코어파일을 남기는 방법부터 알아보자.

 

우선 일부러 segmentation fault 를 내는 코드를 작성해서 실행해본다.

#include <iostream>
#include <memory>

class Player
{
public:
    Player( int id ) : m_id( id ) {}

    int GetId() { return m_id; }
    void SetId( int id ) { m_id = id; }

private:
    int m_id;
};

int main()
{
    Player player( 1 );
    Player* player_raw_pointer = new Player( 2 );
    std::shared_ptr<Player> player_shared_ptr = std::make_shared<Player>( 3 );
    std::shared_ptr<Player> player_empty;

    // Print Id
    std::cout << player.GetId() << '\n';
    std::cout << player_raw_pointer->GetId() << '\n';
    std::cout << player_shared_ptr->GetId() << '\n';

    // Here Error
    std::cout << player_empty->GetId() << '\n';

    return 0;
}

 

player_empty 가 실제로 가리키고 있는 포인터가 없는데 접근을 하여 segment fault 가 발생하는 코드이다.

 

실행 결과는 다음과 같다.

 

segmentation fault

 

그러나 코어파일은 남지 않는다. ls  명령을 사용해서 코어파일이 있는지 확인해보자.

 

코어파일이 남아있지 않은 현상

 

코어파일이 남지 않는 이유는 일반적으로 코어파일 사이즈에 제한이 걸려있기 때문이다.

해당 사이즈의 제한을 풀어주자. ( ulimit -c unlimited )

 

코어파일이 생성됨을 확인

코어가 나오지 않는 경우, 코어가 남는 경로가 os 별로 다를 수 있으니 확인해보자.

 

ls 의 결과를 보면 core 파일이 생성되어 있는 것을 볼수있다.

 

이상으로 코어남기기 부분을 마친다.

 

2. 에러 위치 찾기

생성된 코어파일과, 해당 코어파일을 생성한 바이너리 파일로 에러 위치를 찾을 수 있다.

 

gdb [binary file] [core file] 명령어를 실행한다.

 

 

아래 (gdb) 가 나오며 커맨드를 입력할 수 있다. 에러난 곳을 찾기 위해서는 where 를 입력한다.

위 결과만 보고는 어디에서 에러가 났는지 정확하게 알기 어렵다.

 

quit 명령어를 사용하여 gdb를 종료하자.

 

디버깅 정보를 찾기 위해서는 컴파일단계에서 옵션을 추가한다

 

g 옵션은 gdb 디버깅에 필요한 정보를 바이너리에 추가해서, 디버깅을 쉽게 할 수 있도록 돕는다.

 

다시 gdb로 들어가 where 명령어를 사용해보자.

 

main 의 29번 째 줄에 있는 Player::GetId를 호출하다 죽었는데, this 의 주소가 0x0 으로 nullptr 에 접근한 것을 확인 할 수 있다.

 

이상으로 에러 위치 찾기를 마친다.

 

3. 클래스 맴버 변수 출력하기

 

gdb 에서 변수를 출력하기 위해서는 p ( print ) 명령을 사용한다.

 

해당 명령을 사용하기 위해서는 변수가 있는 stack 으로 이동해야 하는데, 명령어는 up, down 을 사용한다.

 

up 명령어는 부모쪽으로, down 명령어는 자식쪽으로 이동한다.

 

우리가 확인하고 싶은 player 변수들은 main 함수 안에 있으므로 up 명령어를 사용해 main으로 이동하자.

 

이제 클래스 멤버 변수를 출력해보자.

 

출력 예제를 쉽게 보여주기 위해, 위 예제 코드에 플레이어를 여럿 생성해 두었다.

 

종류별로 알아보자.

 

1. 지역 변수로 선언한 경우

.[맴버변수 이름] 으로 확인이 가능하다.

 

2. 포인터로 선언한 경우

->[맴버변수 이름] 으로 확인이 가능하다.

 

3. 스마트 포인터 변수로 선언한 경우

(*클래스._M_ptr)->[맴버변수 이름] 으로 확인이 가능하다.

 

이상으로 포스팅을 마친다.

댓글