본문 바로가기
CS

gdb 기초 사용방법 - 리눅스에서 C++ 디버깅 실행부터 값 출력까지

by Warehaus 2022. 12. 6.

이 고양이는 내용과는 무관합니다..

 

 

gdb란?

 

GDB는 GNU project의 디버거로, 실행되는 상황 또는 충돌이 발생한 프로그램에서 일어난 일들을 볼 수 있도록 한다.

 

GDB, the GNU Project debugger, allows you to see what is going on `inside' another program while it executes -- or what another program was doing at the moment it crashed.

 

출처 : https://www.sourceware.org/gdb/

 

GDB: The GNU Project Debugger

GDB: The GNU Project Debugger [bugs] [GDB Maintainers] [contributing] [current git] [documentation] [download] [home] [irc] [links] [mailing lists] [news] [schedule] [song] [wiki] GDB: The GNU Project Debugger What is GDB? GDB, the GNU Project debugger, al

www.sourceware.org

 

 

GDB가 지원하는 언어

 

  • Ada
  • Assembly
  • C
  • C++
  • D
  • Fortran
  • Go
  • Objective-C
  • OpenCL
  • Modula-2
  • Pascal
  • Rust

 

C, C++, Go, Rust가 눈에 띕니다.  

저는 C++을 주로 사용하기 때문에, 코어가 떨어졌을 때 혹은 gdb를 실행 중인 프로세스에 붙여서 사용합니다.

 

 

 

GDB 실행하기

 

gdb는 실행 중인 프로세스 또는 코어파일을 통해 실행할 수 있습니다.

테스트 코드를 하나 만들어서 붙여볼까 합니다.

 

 

10초의 sleep을 가진 for loop 를 하나 만들었습니다.

 

실행 중인 프로세스에 gdb를 붙여서 i 값이 얼마나 증가했는 지 확인해 보려고 합니다.

 

 

$ ./a.out 
0
1
2

 

 

빌드를 하고 실행하니 10초마다 값이 증가하고 있습니다.

cout을 통해 stdout 출력을 코드에 넣었기 때문에 결과는 예상한대로 수행되고 있습니다.

 

이제 gdb를 붙여볼 차례입니다.

 

 

pid 2941 번에 gdb를 붙여보겠습니다.

 

붙이는 방법은 두 가지가 있습니다.

 

1. 빌드 된 실행파일과 pid
2. -p 옵션과 pid

 

$ gdb <exec_filepath> <pid>

$ gdb -p <pid>

 

 

ptrace: Operation not permitted 

 

라는 에러를 만났습니다.

 

해야 할 일이 두 가지가 있습니다.

ptrace_scope 값을 0으로 우선 변경해 줍니다.

 

 echo 0 > /proc/sys/kernel/yama/ptrace_scope

 

그리고 10-ptrace.conf를 확인해 보도록 합니다.

 

$ sudo vim /etc/sysctl.d/10-ptrace.conf

 

10-ptrace.conf 파일에서 아래 값을 0으로 변경해 줍니다.

 

kernel.yama.ptrace_scope = 0

 

다시한번 프로세스에 gdb를 붙여봅니다.

 

 

조금은 다른 모습을 보여줍니다.

 

backtrace(bt) 명령을 입력하면 지금 어디에서 멈춰있는 지 알 수 있습니다.

 

 

sleep 에서 멈춰있네요. 이 때 프로그램은 멈춰있는 상태입니다.

 

advance 명렁을 통해 다시 프로그램을 실행시켜 봅니다.

 

 

루프가 다시 돌기 시작합니다.

 

지금까지 gdb를 실행해서 간단한 명령을 입력 해 보았는데요,

그래서 어떻게 디버깅을 하라는거지? 라는 궁금증이 생기기 마련입니다.

 

어... 궁금하지 않으실수 도 있는데요..

이렇게 실행만 되어서는 지금 프로그램이 어떤 값을 가지고 수행되고 있는지 알 수가 없기에...

아직까지는 디버깅의 영역에 들어오지 못 했다고 볼 수 있겠습니다.

 

이제 아주 쉬운 예제로 디버깅 이라는 것을 해 보겠습니다.

 

 

 

gdb로 디버깅

 

gdb로 디버깅을 하기 전 준비해야 하는 부분이 있습니다.

g++ 로 빌드 시 '-g' 옵션으로 빌드를 해야 코드를 보면서 빌드가 가능합니다.

 

아래 페이지의 Debuggin option부분을 참고하시면 도움이 됩니다.

 

https://man7.org/linux/man-pages/man1/g++.1.html 

 

g++(1) - Linux manual page

 

man7.org

 

-g  Produce debugging information in the operating system's
           native format (stabs, COFF, XCOFF, or DWARF).  GDB can work
           with this debugging information.

           On most systems that use stabs format, -g enables use of
           extra debugging information that only GDB can use; this extra
           information makes debugging work better in GDB but probably
           makes other debuggers crash or refuse to read the program.
           If you want to control for certain whether to generate the
           extra information, use -gstabs+, -gstabs, -gxcoff+, -gxcoff,
           or -gvms (see below).

 

-g 옵션 외에도 다른 디버깅 옵션이 많지만 지금은 기본적인 gdb 사용에 대해 다루는 글이니 -g 를 이용해서 빌드 해 보도록 하겠습니다.

 

저는 아래의 코드를 빌드하였습니다.

 

#include <stdio.h>
#include <unistd.h>
#include <iostream>

using namespace std;

void sleeping() 
{
    for ( int i = 0; i < 100 ; i++ )
    {
        cout << i << endl;
        sleep( 10 );

    }
}

int main( ){
    sleeping();
    return 0;
}

 

빌드를 해 봅니다.

 

g++ -g gdb.cc

 

이제 프로그램을 실행하고 프로세스에 gdb 를 붙여봅니다.

 

$ gdb -p 10651
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
Attaching to process 10651
Reading symbols from /home/warehouse/Desktop/a.out...
Reading symbols from /lib/x86_64-linux-gnu/libstdc++.so.6...
(No debugging symbols found in /lib/x86_64-linux-gnu/libstdc++.so.6)
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug/.build-id/69/389d485a9793dbe873f0ea2c93e02efaa9aa3d.debug...
Reading symbols from /lib/x86_64-linux-gnu/libm.so.6...
Reading symbols from /usr/lib/debug/.build-id/27/e82301dba6c3f644404d504e1bb1c97894b433.debug...
Reading symbols from /lib64/ld-linux-x86-64.so.2...
Reading symbols from /usr/lib/debug/.build-id/61/ef896a699bb1c2e4e231642b2e1688b2f1a61e.debug...
Reading symbols from /lib/x86_64-linux-gnu/libgcc_s.so.1...
(No debugging symbols found in /lib/x86_64-linux-gnu/libgcc_s.so.1)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007fe28dbf17fa in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffdfe131320, rem=rem@entry=0x7ffdfe131320) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78	../sysdeps/unix/sysv/linux/clock_nanosleep.c: No such file or directory.

 

backtrace  를 시도합니다.

 

A  backtrace is a summary of how your program got where it is.

 

backtrace는 프로그램이 수행하고 있는 위치의 정보를 보여줍니다.

 

(gdb) bt
#0  0x00007fe28dbf17fa in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffdfe131320, 
    rem=rem@entry=0x7ffdfe131320) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
#1  0x00007fe28dbf66e7 in __GI___nanosleep (req=req@entry=0x7ffdfe131320, rem=rem@entry=0x7ffdfe131320)
    at ../sysdeps/unix/sysv/linux/nanosleep.c:25
#2  0x00007fe28dbf661e in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55
#3  0x000055e11b7d320e in sleeping () at gdb.cc:12
#4  0x000055e11b7d3229 in main () at gdb.cc:18

 

sleeping 함수를 실행하고 있는 3번 프레임 코드를 list 명령으로 확인해 봅니다.

 

 

어떤 코드가 돌고있는지 볼 수 있습니다.

 

저는 이번 테스트에서 for loop 의 i 값을 확인해 보려고 합니다.

예시는 너무 단순한 코드이기는 합니다. 그저 숫자 값의 증가만을 보여주기 때문이죠.

하지만, 실제 production tool 에서는 복잡한 함수들의 결과가 될 것입니다.

 

 

p(print) 명령으로 값을 출력해 봅니다.

초기 값이니 i 는 0 이 나옵니다.

 

 

아직 i++ 가 실행되지 않았기에 0 입니다.

step을 이용해 조금 더 코드를 돌려보겠습니다.

 

 

조금 돌리고 나니 i 값이 2가 찍힙니다.

 

앞에서 i 값은 for loop 시작 전, i = 0 에서 초기화 된 값을 의미합니다.

그 다음 line 에서 i 는 i++ 에 의해 1 이 할당되어 루프를 돕니다.

i 값이 2가 나온 이유는 i++ 가 두번 실행된 결과입니다.

 

 

마치며

 

gdb 에서 할 수 있는 것들은 사용자에 따라서 정말 격차가 심하게 납니다.

저도 완전 초보자 수준이라 값 찍어보고 core dump 분석정도만 하는데

이것만 해도 정말 많은 일들을 할 수가 있습니다.

 

시니어 개발자들 gdb쓰는 걸 보면

파이썬을 이용해서 각종 gdb도구를 만들어서 수행도 시키는데

아직 저는 그정도 수준에는 도달하지 못했습니다.

 

그런데도 너무 유용한 도구이기에

잘 사용하고 있고,

C, C++ 개발을 조금이라도 하신다면

잘쓰면 잘 쓸수록 좋다는게 제 생각입니다.

 

 

물론.. 요즘 커리어를 C,C++로 시작한다는건 다소 슬픈 일이기는 합니다.

 

 

 

 

 

wh.