극히 개인적이고 극히 대단하지 않은

요긴한 gdb 명령들 본문

20년차 개발자

요긴한 gdb 명령들

회색싼타 2021. 5. 20. 21:46

C로 프로그램을 작성하고 시험을 하다보면 수없이 많은 core 파일을 만나게 되는 경우가 있다. core 파일은 개발자 입장에서 별로 만나고 싶지 않은 파일이긴 하지만, 정상적으로 동작하지 않는 프로그램과 긴긴 시간을 씨름할 때에 있어서 core 파일은 때로는 사막에서 오아시스를 만나듯이 반가운 경우도 있다.

 

core파일을 얼마나 능수능란하게 다루고 요긴한 명령들을 조합해서 잘 쓰는가에 따라 core파일은 개발자에게 문제를 해결할 수 있는 명쾌하고 직관적인 정보를 주기도 하지만, 그렇지 않은 경우에는 문제점에 더불어 해결해야 할 숙제를 하나 더 떠안은 것 같은 부담으로 작용할 수도 있다.

 

core파이을 분석하기 위해 다양한 명령어를 쓰지만, 흔히 쓰지 않으면서도 알고 있으면 도움이 될만한 명령을 소개한다.

 

1. whatis

변수의 타입을 알려준다.

core파일을 디버깅하다보면 다양한 변수를 마주하게 되는데, 각 변수의 타입이 뭔지 명확하지 않으면 분석이 어려워질 수 있고, 소스를 옆에 열어놓고 변수의 선언부를 함께 확인해야 하는 번거로움이 있을 수 있는데, 이 명령은 그러한 번거로움을 깔끔하게 해결해준다.

해당 변수가 구조체인 경우 구조체의 정보를 알려준다.

 

(gdb) where
#0  0x0000000000000001 in ?? ()
#1  0x00007f02e9307471 in mariadb_reconnect () from /lib/x86_64-linux-gnu/libmariadb.so.3
#2  0x00007f02e9307bc1 in ?? () from /lib/x86_64-linux-gnu/libmariadb.so.3
#3  0x00007f02e9307e75 in mysql_ping () from /lib/x86_64-linux-gnu/libmariadb.so.3
#4  0x000055556e77becc in initR_CDR_DATA (mysql=0x0, endTime=0x7ffdfc288790 "2021-05-20 19:50:04") at appl/sip/TSM/db_util.c:1448
#5  0x000055556e6ed161 in bt_sighandler (sig=11, info=0x7ffdfc2889b0, secret=0x7ffdfc288880) at appl/sip/TSM/BSM_main.c:523
#6  <signal handler called>
#7  0x00007f02e9115028 in __GI__IO_fread (buf=0x5555716b7033, size=1, count=5, fp=0x555570f95da0) at iofread.c:37
#8  0x00007f02e940789e in file_read () from /usr/local/lib/libcrypto.so.1.1
#9  0x00007f02e9403c3e in bread_conv () from /usr/local/lib/libcrypto.so.1.1
#10 0x00007f02e9402806 in BIO_read () from /usr/local/lib/libcrypto.so.1.1
#11 0x00007f02e9663e69 in ssl3_read_n () from /usr/local/lib/libssl.so.1.1
#12 0x00007f02e966880e in ssl3_get_record () from /usr/local/lib/libssl.so.1.1
#13 0x00007f02e9665ef8 in ssl3_read_bytes () from /usr/local/lib/libssl.so.1.1
#14 0x00007f02e969ca01 in tls_get_message_header () from /usr/local/lib/libssl.so.1.1
#15 0x00007f02e969266d in state_machine.part () from /usr/local/lib/libssl.so.1.1
#16 0x00007f02e967d0c8 in SSL_do_handshake () from /usr/local/lib/libssl.so.1.1
#17 0x000055556ea8aa1b in RvTLSSessionServerHandshake (tlsSession=0x7f02dc235700, certCB=0x0, tcpSock=104, mtx=0x7f02db188420, logMgr=0x555570f95da0) at common/ccore/netdrivers/rvtls.c:1397
#18 0x000055556ea249d6 in TlsContinueHandshake (pConn=0x7f02db188318) at sip/transport/TransportTLS.c:1089
#19 0x000055556ea230b1 in TransportTLSHandleReadWriteEvents (pConn=0x7f02db188318, event=2) at sip/transport/TransportTLS.c:371
#20 0x000055556ea21337 in TransportTcpEventCallback (selectEngine=0x555570f9b098, fd=0x7f02db188320, selectEvent=2, error=0) at sip/transport/TransportTCP.c:1271
#21 0x000055556ea7c9d4 in RvSelectHandleSingleSelectFd (selectEngine=0x555570f9b098, selectEvent=2, hasError=0, fd=104) at common/ccore/netdrivers/rvselect.c:1909
#22 0x000055556ea7caca in RvSelectHandleEpollFds (selectEngine=0x555570f9b098, epFdSet=0x7f02dca03078, numFds=44016, numEvents=1) at common/ccore/netdrivers/rvselect.c:2234
#23 0x000055556ea7fc33 in RvSelectWaitAndBlock (selectEngine=0x555570f9b098, nsecTimeout=100000000000000000) at common/ccore/netdrivers/rvselect.c:5285
#24 0x000055556ea7f738 in RvSelectProcessEvents (selectEngine=0x555570f9b098) at common/ccore/netdrivers/rvselect.c:4715
#25 0x000055556e94cd32 in RvSipStackProcessEvents () at sip/stackMgr/RvSipStack.c:1893
#26 0x000055556e6f3641 in main (argc=2, argv=0x7ffdfc289ab8) at appl/sip/TSM/BSM_main.c:3574
(gdb)

 

 

예를 들어 위와 같은 core 파일이 있다고 가정할 때, 17번 frame에서 SSL_do_handshake() 를 호출했고 결국은 프로세스가 비정상 종료되었다면 다음과 같이 whatis 명령어로 필요한 frame에서 변수들의 type을 확인할 수 있다.

(gdb) frame 17
#17 0x000055556ea8aa1b in RvTLSSessionServerHandshake (tlsSession=0x7f02dc235700, certCB=0x0, tcpSock=104, mtx=0x7f02db188420, logMgr=0x555570f95da0) at common/ccore/netdrivers/rvtls.c:1397
1397	in common/ccore/netdrivers/rvtls.c
(gdb) whatis tlsSession
type = RvTLSSession *

 

2. ptype

변수의 타입과, 변수가 구조체로 선언되어 있는 경우 구조체를 구성하는 구성요소들을 확인할 수 있다.

(gdb) ptype tlsSession
type = struct {
    SSL *sslSession;
    BIO *bio;
    RvSocket sock;
    RvSelectEvents requiredForHandshake;
    RvSelectEvents requiredForTLSRead;
    RvSelectEvents requiredForTLSWrite;
    RvSelectEvents requiredForTLSShutdown;
    RvTLSEvents pendingTlsEvents;
    RvTLSRenegState renegState;
    RvBool bWaitingShutdownFinish;
    RvUint32 counterWrites;
} *

 

3. set print pretty on

복잡한 구조체의 내용을 print 명령으로 출력하다보면 줄맞춤이 되지않아 보기 어려울 때 요긴하다. 특히 구조체 내에 다른 구조체를 포함한 경우에 가독성을 훨씬 높여준다.

(gdb) p *tlsSession
$2 = {sslSession = 0x5555716adba0, bio = 0x5555716af850, sock = 104, requiredForHandshake = 0, requiredForTLSRead = 0, requiredForTLSWrite = 0, requiredForTLSShutdown = 0, pendingTlsEvents = 0, renegState = RV_TLS_RENEG_NONE, 
  bWaitingShutdownFinish = 0, counterWrites = 0}
(gdb) set print pretty on
(gdb) p *tlsSession
$3 = {
  sslSession = 0x5555716adba0,
  bio = 0x5555716af850,
  sock = 104,
  requiredForHandshake = 0,
  requiredForTLSRead = 0,
  requiredForTLSWrite = 0,
  requiredForTLSShutdown = 0,
  pendingTlsEvents = 0,
  renegState = RV_TLS_RENEG_NONE,
  bWaitingShutdownFinish = 0,
  counterWrites = 0
}
(gdb) 

 

4.  set print elements

 

디버깅을 하다보면 문자열이나 array의 길이가 다 출력되지 않고 잘려서 출력되는 경우가 있다. (...으로 잘림)

이런 경우에는 set print elements 뒤에 원하는 출력길이를 명시하면 그 길이까지 출력이 된다. 

 

(gdb) p ph->data
$6 = 0x29157d0 "OPTIONS sip:jaOPwpQz@211.104.XXX.XXX SIP/2.0\r\nVia: SIP/2.0/UDP 192.241.207.17:45540;branch=zBrjvB.4690431032;rport;alias\r\nFrom: sip:MJagrVeB@192.241.207.17:45540;tag=41328117\r\nTo: sip:GPLWZfXH@211.104."...
(gdb) set print elements 1024
(gdb) p ph->data
$7 = 0x29157d0 "OPTIONS sip:jaOPwpQz@211.104.XXX.XXX SIP/2.0\r\nVia: SIP/2.0/UDP 192.241.207.17:45540;branch=zBrjvB.4690431032;rport;alias\r\nFrom: sip:MJagrVeB@192.241.207.17:45540;tag=41328117\r\nTo: sip:GPLWZfXH@211.104.XXX.XXX\r\nCall-ID: 4461338609@192.241.207.17\r\nCSeq: 1 OPTIONS\r\nContact: sip:mYaZFuNs@192.241.207.17:45540\r\nContent-Length: 0\r\nMax-Forwards: 20\r\nUser-Agent: rOHCvKym\r\nAccept: text/plain\r\n\r"

 

Comments