[리눅스/유닉스] 프로그램 장애 확인 및 처리
프로그램 코드를 짤 때 가장 중요한 것은 예외 처리를 하는 것입니다.
하지만 최대한 예외 처리 또는 에러 처리를 진행한다 해도 저희가 생각하지도 못한 곳에서 에러가 발생하거나 못 보고 지나칠 경우도 존재합니다.
그런 경우에는 크게 두 가지 현상이 발생합니다.
1. 컴파일하면서 에러 발생
컴파일 에러인 경우, 컴파일을 할 때 에러가 발생한 위치를 정확히 알려주어 비교적 쉽게 수정하여 해결이 가능합니다.
(이때, warning으로 발생되는 코드도 무시하시지 말고 해결하여 컴파일하길 권장합니다.)
2. 프로그램 실행 도중 에러 발생
만약 컴파일이 완료가 되어 실행 프로그램이 생성되었다 하더라도 실행 프로그램이 돌다가 죽어버리는 현상이 발생하며 이때 코어 파일 사이즈가 설정이 되어있다면 코어 파일이 생성됩니다.
만약, 발생하지 않는다면 코어 파일 사이즈를 확인하시고 늘려주셔야 합니다.
ulimit -a /* 코어 파일 사이즈 확인 */
[won@localhost ~]$ ulimit -a
core file size (blocks, -c) 0
....
ulimit -c unlimited /* unlimited로 코어파일 사이즈 변경 */
[won@localhost ~]$ ulimit -a
core file size (blocks, -c) unlimited
....
웬만하면 unlimited로 하는 편을 추천합니다.
< 간혹 코어 파일 사이즈를 설정해도 코어 파일이 떨어지지 않을 때가 있습니다. 이때는 다른 방법으로 디버그를 돌려봐야 합니다. 저는 보통 log를 찍어가며 디버깅을 합니다. >
- 코어 파일을 통한 디버깅
gdb(GNU Debugger)
이 프로그램을 사용하기 위해서는 컴파일 과정에서 -g 옵션을 줘야 합니다.
gdb [프로그램 파일] [코어 파일]을 주어 gdb 안에 들어가 where라는 명령어를 치면 해당 프로그램이 어디서 죽었는지 알 수 있습니다.
이때 각 코어 파일과 프로그램의 연관성을 확인하기 위해 file 명령어를 이용합니다.
< gdb는 다양한 디버깅을 할 수 있지만 저는 코어 파일 확인용으로만 사용합니다. >
[won@localhost test]$ file core.10395 /* file 명령어를 통해 프로그램 확인 */
core.10395: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './test', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: './test', platform: 'x86_64'
[won@localhost test]$ gdb test core.10395 /* gdb 명령어 사용 */
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
Copyright (C) 2013 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-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/won/test/test...done.
[New LWP 10395]
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
#0 0x00007f5600d34004 in __memcpy_ssse3_back () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7.x86_64
(gdb) where /* 위치 탐색 */
#0 0x00007f5600d34004 in __memcpy_ssse3_back () from /lib64/libc.so.6
#1 0x000000000040056a in main (argc=1, argv=0x7fff814fcf18) at test.c:10
(gdb) q /* 나가기 */
[won@localhost test]$
dbx
gdb와 비슷한 디버거입니다. 대체적으로 유닉스에서 사용하며 사용법은 gdb와 동일합니다. (코어 파일 분석인 경우)
- 자주 사용하는 디버깅 방법
이제부터 소개해드릴 방법은 제가 회사 제품에 문제가 생겼을 경우 코어 파일 분석 외 디버깅을 해보는 방법입니다.
LOG
의심되는 코드 부분에 해당 값과 에러 번호(errno)를 찍어 log를 남깁니다.
pstack / procstack
둘 다 스택 출력 명령어입니다. procstack은 Sun과 AIX OS 사용되며 pstack은 리눅스와 그 외 유닉스에서 사용됩니다.
[won@localhost ~]$ pstack 9905 /* pid 9905 입력 */
#0 0x00007f95ec6fdfd0 in __write_nocancel () from /lib64/libc.so.6 /* 마지막 실행 */
#1 0x00007f95ec6890b3 in _IO_new_file_write () from /lib64/libc.so.6
#2 0x00007f95ec68a8ce in __GI__IO_do_write () from /lib64/libc.so.6
#3 0x00007f95ec689810 in __GI__IO_file_xsputn () from /lib64/libc.so.6
#4 0x00007f95ec659a33 in vfprintf () from /lib64/libc.so.6
#5 0x00007f95ec662329 in printf () from /lib64/libc.so.6
#6 0x0000000000400f7f in DataSort (c_list=0x7ffc175f5968, count=37) at h_project_1.c:195
#7 0x0000000000401368 in main (argc=1, argv=0x7ffc175f5e68) at h_project_1.c:319 /* 처음 실행 */
strace / truss / tusc
strace는 리눅스와 sun에서 많이 사용되는 유틸리티이며, truss는 solaris와 AIX, tusc는 HP-UX에서 사용됩니다.
특정 프로그램(프로세스)의 system call이나 signal을 추적하는 동적 분석 유틸이라고 생각하시면 됩니다.
여러 옵션들이 존재하지만 저는 -p 옵션을 자주 사용합니다.
strace 옵션
-e 옵션(-e trace=x,x,x…) : 특정 시스템 콜 확인
-o 옵션 : 파일로 저장 가능
-c 옵션 : 시스템 콜 통계 내역 확인
-p 옵션 : 실행 중인 프로그램 확인(-f)
-t 옵션 : 각 trace에 대한 시간 정보 출력
-r 옵션 : 각 시스템 콜의 상대 시간 정보 출력
-h 옵션 : 도움말
[won@localhost ~]$ ps -ef | grep won /* 프로세스 검색 */
won 10871 9762 12 20:05 pts/0 00:00:00 ./h_project_1
won 10872 9914 0 20:05 pts/1 00:00:00 ps -ef
won 10873 9914 0 20:05 pts/1 00:00:00 grep --color=auto won
[won@localhost ~]$ strace -p 10871 /* 검색한 프로세스를 추적 시작 */
strace: Process 10871 attached
write(1, "\n", 1) = 1
write(1, "current front : (0xb3e2f0) currn"..., 73) = 73
write(1, "current front : (0xb3e2f0) currn"..., 73) = 73
^C /* 추적 종료 => ctrl + c || 프로세스가 종료되었을 경우 */
strace: Process 10871 detached
※ 제 글에서 부족하거나 잘못된 부분이 있다면 댓글로 남겨주시길 부탁드립니다.
※ 제 글은 제가 코딩하면서 지속해서 확인하기 위한 히스토리 성 정보들이자 회사 생활 중 실제 프로그램을 코딩하며 중요하거나 필요했던 정보들을 공유하기 위해 적은 글입니다.