Linux에서 디버깅을 위한 코어 파일을 보는 방법은 무엇입니까?

Linux에서 디버깅을 위한 코어 파일을 보는 방법은 무엇입니까?

프로그램을 디버깅하는 동안 코어 파일의 내용을 보고 싶습니다. 코어 파일의 내용을 보는 방법은 무엇입니까?

답변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

댓글 영역에는 더 많은 메타데이터도 있습니다.특히 prstatusPC를 포함한:

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

명령줄 도구를 사용하려는 경우 다음을 사용할 수 있습니다.데이터 베이스:

gdb <program> <core file>

또는

gdb <program> -c <core file>

GUI가 마음에 든다면 설치하세요디디을 클릭한 다음 디버깅하려는 프로그램과 코어 파일을 엽니다.

답변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

관련 정보