프로그램을 디버깅하는 동안 코어 파일의 내용을 보고 싶습니다. 코어 파일의 내용을 보는 방법은 무엇입니까?
답변1
GDB 최소 실행 가능 예제
GDB는 이전에 다음에서 언급되었습니다:https://unix.stackexchange.com/a/89934/32558이 답변을 찬성하는 것을 고려하십시오.
간단한 c
int myfunc(int i) {
*(int*)(0) = i;
return i - 1;
}
int main(int argc, char **argv) {
(void)argv;
int i = argc * 2;
int ret = myfunc(i);
return ret;
}
컴파일하고 실행하여 코어를 생성합니다.
gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o simple.out simple.c
코어 파일을 생성하려면 먼저 현재 터미널에서 실행해야 합니다.
ulimit -c unlimited
이는 "크기 제한 없이 코어 파일 덤프"를 의미합니다. 이는 코어 파일에 충돌이 발생한 프로세스의 전체 메모리가 포함되어 크기가 매우 클 수 있기 때문에 발생합니다.
테스트를 위해 Ubuntu 16.04부터 기존 코어 파일을 삭제해야 합니다(TODO 필수? 잊어버렸습니다).
rm -f core
Ubuntu 22.04부터 핵심 파일을 얻으려면 apport와 싸워야 합니다.https://askubuntu.com/questions/1349047/where-do-i-find-core-dump-files-and-how-do-i-view-and-analyze-the-backtrace-st/1442665#1442665예를 들어:
echo 'core' | sudo tee /proc/sys/kernel/core_pattern
그런 다음 프로그램을 실행합니다.
./simple.out
터미널에는 다음이 포함됩니다.
Segmentation fault (core dumped)
코어 파일이 생성되었습니다. Ubuntu 16.04에서 파일 이름은 다음과 같습니다.
core
Ubuntu 22.04에서 echo 'core' | sudo tee /proc/sys/kernel/core_pattern
파일 이름은 다음과 같습니다.
core.<pid>
여기서 PID는 프로세스 ID, 즉 숫자입니다. 예:
core.162152
Linux 커널 업데이트가 접미사 추가를 시작했기 때문이라고 생각합니다 .pid
. 할 일 확인.
이제 코어 파일을 다음과 같이 사용할 수 있습니다.
gdb simple.out core
gdb simple.out core.162152
이제 우리는 GDB 세션에 들어갑니다. 이는 프로그램이 충돌했을 때와 정확히 같은 상황이지만 프로그램이 곧 끝나기 때문에 "계속 실행"할 수 없습니다.
#0 0x0000557097e0813c in myfunc (i=2) at simple.c:2
2 *(int*)(0) = i; /* line 7 */
(gdb) bt
#0 0x0000557097e0813c in myfunc (i=2) at simple.c:2
#1 0x0000557097e0816b in main (argc=1, argv=0x7ffcffc4ba18) at simple.c:9
(gdb) up
#1 0x0000557097e0816b in main (argc=1, argv=0x7ffcffc4ba18) at simple.c:9
9 int ret = myfunc(i);
(gdb) p argc
$1 = 1
따라서 실행 후 bt
충돌이 발생했을 때 코드가 어디에 있었는지 즉시 알 수 있으며 때로는 오류를 수정하기에 충분합니다.
예제에서 볼 수 있듯이 이제 충돌 시 프로그램 메모리를 검사하여 오류의 원인을 확인할 수 있으며, 프로세스 가상 메모리는 모두 코어 파일에 포함되어 있습니다.
Ubuntu 16.04 및 22.04 amd64에서 테스트되었습니다.
GDB를 통해 직접 프로그램을 실행할 수도 있습니다.
문제가 쉽게 재현 가능하고(즉, 빠르고 결정적으로 충돌이 발생함) 명령줄을 쉽게 제어할 수 있는 경우(즉, 수정을 원하지 않거나 수정할 수 없는 다른 프로그램에서 호출한 프로그램이 아님) 가장 좋은 방법은 다음과 같습니다. GDB를 통해 프로그램을 실행합니다:
gdb -ex run simple.out
신호가 수신되면 GDB는 기본적으로 신호 원인으로 인해 중단되며 코어 파일을 사용할 때와 똑같은 상황에 처하게 됩니다.
직접 Binutils 분석
더 잘 이해하기 위해 GDB 없이 코어 파일의 내용을 관찰해 보겠습니다. 우리가 할 수 있기 때문에.
사물의 상관관계를 확인할 수 있도록 자체 메모리 주소를 인쇄하는 프로그램을 만들어 보겠습니다.
메인 프로그램
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int myfunc(int i) {
*(int*)(NULL) = i; /* line 7 */
return i - 1;
}
int main(int argc, char **argv) {
/* Setup some memory. */
char data_ptr[] = "string in data segment";
char *mmap_ptr;
char *text_ptr = "string in text segment";
(void)argv;
mmap_ptr = (char *)malloc(sizeof(data_ptr) + 1);
strcpy(mmap_ptr, data_ptr);
mmap_ptr[10] = 'm';
mmap_ptr[11] = 'm';
mmap_ptr[12] = 'a';
mmap_ptr[13] = 'p';
printf("text addr: %p\n", text_ptr);
printf("data addr: %p\n", data_ptr);
printf("mmap addr: %p\n", mmap_ptr);
/* Call a function to prepare a stack trace. */
return myfunc(argc);
}
프로그램 출력:
text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)
첫 번째:
file core
core
파일이 실제로 ELF 파일임을 알려줍니다 .
core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './main.out'
그렇기 때문에 일반적인 binutils 도구를 사용하여 더 직접적으로 검사할 수 있습니다.
빨리 살펴보세요ELF 표준실제로 전용 ELF 유형이 있음을 보여줍니다.
Elf32_Ehd.e_type == ET_CORE
자세한 형식 정보는 다음에서 확인할 수 있습니다.
man 5 core
그 다음에:
readelf -Wa core
파일 구조에 대한 몇 가지 힌트를 제공합니다. 메모리는 일반 프로그램 헤더에 포함된 것 같습니다.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NOTE 0x000468 0x0000000000000000 0x0000000000000000 0x000b9c 0x000000 0
LOAD 0x002000 0x0000000000400000 0x0000000000000000 0x001000 0x001000 R E 0x1000
LOAD 0x003000 0x0000000000600000 0x0000000000000000 0x001000 0x001000 R 0x1000
LOAD 0x004000 0x0000000000601000 0x0000000000000000 0x001000 0x001000 RW 0x1000
댓글 영역에는 더 많은 메타데이터도 있습니다.특히 prstatus
PC를 포함한:
Displaying notes found at file offset 0x00000468 with length 0x00000b9c:
Owner Data size Description
CORE 0x00000150 NT_PRSTATUS (prstatus structure)
CORE 0x00000088 NT_PRPSINFO (prpsinfo structure)
CORE 0x00000080 NT_SIGINFO (siginfo_t data)
CORE 0x00000130 NT_AUXV (auxiliary vector)
CORE 0x00000246 NT_FILE (mapped files)
Page size: 4096
Start End Page Offset
0x0000000000400000 0x0000000000401000 0x0000000000000000
/home/ciro/test/main.out
0x0000000000600000 0x0000000000601000 0x0000000000000000
/home/ciro/test/main.out
0x0000000000601000 0x0000000000602000 0x0000000000000001
/home/ciro/test/main.out
0x00007f8d939ee000 0x00007f8d93bae000 0x0000000000000000
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93bae000 0x00007f8d93dae000 0x00000000000001c0
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93dae000 0x00007f8d93db2000 0x00000000000001c0
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93db2000 0x00007f8d93db4000 0x00000000000001c4
/lib/x86_64-linux-gnu/libc-2.23.so
0x00007f8d93db8000 0x00007f8d93dde000 0x0000000000000000
/lib/x86_64-linux-gnu/ld-2.23.so
0x00007f8d93fdd000 0x00007f8d93fde000 0x0000000000000025
/lib/x86_64-linux-gnu/ld-2.23.so
0x00007f8d93fde000 0x00007f8d93fdf000 0x0000000000000026
/lib/x86_64-linux-gnu/ld-2.23.so
CORE 0x00000200 NT_FPREGSET (floating point registers)
LINUX 0x00000340 NT_X86_XSTATE (x86 XSAVE extended state)
objdump
모든 메모리는 쉽게 덤프될 수 있습니다.
objdump -s core
여기에는 다음이 포함됩니다.
Contents of section load1:
4007d0 01000200 73747269 6e672069 6e207465 ....string in te
4007e0 78742073 65676d65 6e740074 65787420 xt segment.text
Contents of section load15:
7ffec6739220 73747269 6e672069 6e206461 74612073 string in data s
7ffec6739230 65676d65 6e740000 00a8677b 9c6778cd egment....g{.gx.
Contents of section load4:
1612010 73747269 6e672069 6e206d6d 61702073 string in mmap s
1612020 65676d65 6e740000 11040000 00000000 egment..........
이는 우리 실행의 표준 출력 값과 정확히 일치합니다.
Ubuntu 16.04 amd64, GCC 6.4.0, binutils 2.26.1에서 테스트되었습니다.
Mozilla rr
역디버깅은 궁극적인 "코어 파일"입니다.
코어 파일을 사용하면 중단 시 스택을 검사할 수 있습니다.
그러나 일반적으로 실제로 해야 할 일은 시간을 거슬러 올라가 실패의 근본 원인을 더 자세히 파악하는 것입니다.
놀라운 Mozilla rr을 사용하면 이를 수행할 수 있지만 추적 파일이 더 커지고 성능이 약간 저하됩니다.
예는 다음과 같습니다.https://stackoverflow.com/questions/1470434/how-does-reverse-debugging-work/53063242#53063242
당신은 또한 볼 수 있습니다
답변2
gdb는 코어 파일을 검사하는 데 사용할 수 있는 GNU 디버거입니다. BTW bt
(역추적)는 프로그램 호출 스택을 검사하는 데 유용한 gdb 명령입니다.
gdb binary-file core-file
답변3
답변4
#-------------------------------------------------------------------------
#!/usr/bin/ksh
# -------------------------------------------------------------------------
_OUTFILE=XXXX-XXXX-Audit-`date +"%Y%m%d%H%M"`.log
>$_OUTFILE
MAILLIST=""
COREPATH=$PKMS/logs/cores
MARKER=$COREPATH/marker
function Parse
{
while getopts :p:u:s:l: name
do
case $name in
p) PKMS="$OPTARG" ;; # $PKMS
u) DBUSER="$OPTARG" ;; # $DBUSER
s) DBPSWD="$OPTARG" ;; # $DBPSWD
l) DBLOCN="$OPTARG" ;; # $DBLOC
*) Usage ;; # display usage and exit
esac
done
if [[ -z "${PKMS}" || -z "${DBUSER}" || -z "${DBPSWD}" || -z "${DBLOCN}" ]]
then
echo $Usage
exit -1
fi
}
function getCoreDumps
{
COREFILES=$COREPATH/newcores.txt
STACKS=$COREPATH/stacks.txt
DATE=$(date +%y%m%d%H%M%S)
>$COREFILES
>$STACKS
umask 002
find $COREPATH -type f -newer $MARKER -name "core" > $COREFILES
find $COREPATH -type f -newer $MARKER -name "core.?" >> $COREFILES
rm $STACKS 2>/dev/null
for i in $(<$COREFILES)
do
mv $i $i.$DATE
chmod g+r,g+w $i.$DATE
#echo "Coredump recently found at" `date` '\n'>> $STACKS
echo $i.$DATE >> $STACKS
#echo >> $STACKS
done
NL=$(wc -l $COREFILES | awk '{ print $1 }')
if [ "$NL" -gt 0 ]
then
echo "New CORE files found:" >> $_OUTFILE
echo "--- ---- ----- ------" >> $_OUTFILE
cat $STACKS >> $_OUTFILE
else
echo "No new CORE files found" >> $_OUTFILE
echo "-- --- ---- ----- -----" >> $_OUTFILE
fi
}
#/usr/bin/clear
echo "\t\t\t\t---------------------------------\t" >> $_OUTFILE
echo "\t\t\t\t
echo "\t\t\t\t---------------------------------\t" >> $_OUTFILE
date "+ %d/%m/%Y %H:%M:%S" >> $_OUTFILE
echo "===================" >> $_OUTFILE
echo " APPICATION MACHINES" >> $_OUTFILE
echo "===================" >> $_OUTFILE
echo >> $_OUTFILE
echo >> $_OUTFILE
getCoreDumps
echo >> $_OUTFILE
echo >> $_OUTFILE
echo "===================" >> $_OUTFILE
echo "XXXX APP DataBase Info" >> $_OUTFILE
echo "===================" >> $_OUTFILE
echo >> $_OUTFILE
getAPPDBInfo
echo >> $_OUTFILE
echo >> $_OUTFILE
MAILDATE=$(date +%d/%m/%Y)
mailx -s "XXXX Monitor Log for $PKMS Environment - Dated $MAILDATE" $MAILLIST < $_OUTFILE
touch $MARKER
rm /tmp/XXXXtempOUTFILE
exit 0