다음을 통해 공유


Linux 라이브 원격 프로세스 디버깅

이 문서에서는 Linux에 대한 라이브 WinDbg 연결을 설정하는 방법을 설명합니다. Linux에서 실시간 원격 프로세스 디버깅을 수행하려면 WinDbg 버전 1.2402.24001.0 이상이 필요합니다.

GNU 디버거 - GDBServer는 Linux에서 WinDbg 연결을 지원하는 데 사용됩니다. GDBServer에 대한 자세한 내용은 다음을 참조하세요 https://en.wikipedia.org/wiki/Gdbserver. 원격 gdb 디버깅에 대한 설명서를 볼 수 있는 한 곳은 다음과 같습니다. https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

이 예제에서는 WSL(Linux용 Windows 하위 시스템)을 사용하지만 다른 Linux 구현도 사용할 수 있습니다.

원격 프로세스 디버깅의 WinDbg 유형

WinDbg를 사용하여 원격 디버깅을 수행하는 두 가지 기본 방법인 프로세스 서버 또는 KD 연결 서버가 있습니다. 프로세스 서버는 사용자 모드 디버깅에 사용됩니다. KD 연결 서버는 커널 모드 디버깅에 사용됩니다. 이러한 WinDbg 연결 유형에 대한 일반적인 내용은 프로세스 서버(사용자 모드)KD 커넥트ion 서버(커널 모드)를 참조하세요.

Linux 사용자 모드 프로세스 디버깅을 시작하는 방법에는 두 가지가 있습니다. 특정 프로세스에서 gdbserver를 시작하거나 기존 프로세스를 나열하고 연결할 수 있는 프로세스 서버gdbserver를 시작할 수 있습니다. 이는 Windows의 DbgSrv(dbgsrv.exe) 프로세스 서버와 비슷합니다. 자세한 내용은 프로세스 서버 활성화를 참조 하세요.

사용자 모드 Linux 프로세스 디버깅

특정 단일 사용자 모드 프로세스 또는 다중 모드에 연결하여 목록의 모든 프로세스를 확인하고 연결할 프로세스를 선택할 수 있습니다. 두 방법 모두이 항목에 설명 되어 있습니다. 두 메서드 모두 다음에 설명된 동일한 연결 문자열 구문을 공유합니다.

gdbserver 연결 문자열 형식

gdbserver에 연결하는 데 사용되는 형식은 인수가 "argument=value"의 쉼표로 구분된 목록인 "protocol:arguments"입니다. 사용자 모드 gdbserver 연결의 경우 프로토콜은 gdb이고 인수 집합은 다음과 같습니다.

server=<address> - 필수: 연결할 gdbserver의 IP 주소를 나타냅니다.

port=<port> - 필수: 연결할 gdbserver의 포트 번호를 나타냅니다.

threadEvents=<true|false> - 선택 사항: 이 버전의 gdbserver에 대한 스레드 이벤트가 중지 모드에서 올바르게 작동하는지 여부를 나타냅니다.

현재 gdbserver 릴리스에는 중지 모드(WinDbg에서 사용하는) 서버에서 스레드 이벤트를 사용하도록 설정하면 gdbserver가 충돌하는 문제가 있습니다. 이 값이 false(기본값)이면 스레드 시작 및 중지 이벤트가 합성되지만 스레드 생성/소멸의 실제 시간보다 훨씬 늦게 나타날 수 있습니다. gdbserver에서 이 문제를 해결할 수 있는 경우 이 옵션을 통해 실제 이벤트를 사용하도록 설정할 수 있습니다.

단일 사용자 모드 프로세스로 커넥트

이 섹션에서는 WinDbg를 사용하여 Linux에서 단일 사용자 모드 프로세스를 식별하고 연결하는 방법을 설명합니다.

WSL(Linux용 Windows 하위 시스템)

이 예제에서는 WSL(Linux용 Windows 하위 시스템)을 사용하지만 다른 Linux 구현을 사용할 수 있습니다. WSL 설정 및 사용에 대한 자세한 내용은 다음을 참조하세요.

원하는 프로세스 선택

명령을 사용하여 ps -A 연결할 실행 중인 프로세스를 결정하는 Linux의 프로세스를 나열합니다.

user1@USER1:/mnt/c/Users/USER1$ ps -A
    PID TTY          TIME CMD
    458 pts/1    00:00:00 bash
    460 ?        00:00:00 rtkit-daemon
    470 ?        00:00:00 dbus-daemon
    482 ?        00:00:19 python3
   1076 ?        00:00:00 packagekitd
   1196 pts/0    00:00:00 ps

이 예제 연습에서는 python3에 연결합니다.

대상 시스템 IP 주소 찾기

원격 Linux 대상에 연결하는 경우 외부 IP 주소를 확인하는 등의 ip route show명령을 사용합니다.

user1@USER1:/mnt/c/Users/USER1$ ip route show
default via 192.168.1.1 dev enp3s0 proto dhcp metric 100
172.25.144.0/24 dev enp3s0 proto kernel scope link src 192.168.1.107 metric 100

이 연습에서는 동일한 PC에서 실행되는 WSL에 연결하고 localhostIP 주소를 사용합니다.

선택한 프로세스에 GDBServer 연결

WSL Linux 콘솔에서 포트 1234에서 gdbserver를 시작하고 python3 프로세스에 연결하기 위해 입력 gdbserver localhost:1234 python3 합니다.

USER1@USER1:/mnt/c/Users/USER1$ gdbserver localhost:1234 python3
Process python3 created; pid = 1211
Listening on port 1234

일부 Linux 환경의 경우 명령을 관리자 권한으로 실행해야 할 수 있습니다(예: sudo 사용 ) . sudo gdbserver localhost:1234 python3 디버거 관리자 루트 수준 액세스를 사용하도록 설정하는 데 주의하고 필요한 경우에만 사용합니다.

WinDbg에서 프로세스 서버 연결 만들기

WinDbg를 열고 "원격 디버거에 대한 파일/커넥트"을 선택하고 연결에 대한 프로토콜 문자열을 입력합니다. 이 예제에서는 다음 gdb:server=localhost,port=1234을 사용합니다.

연결 문자열 보여 주는 WinDbg 디버깅 시작 화면의 스크린샷

확인 단추를 클릭하면 디버거가 gdbserver에 연결되고 초기 프로세스 시작 중단에 있어야 합니다.

초기 중단점에 도달하면 'g'를 여러 번 칠 수 있습니다. 모듈 로드 메시지가 표시됩니다(그리고 sxe 스타일 "모듈 로드 시 중단" 이벤트가 제대로 작동해야 함).

디버그 기호가 캐시에 로드되므로 해당 지점에 도달하는 데 시간이 걸릴 수 있습니다. 기호 서버 또는 로컬 검색 경로를 통해 기호 및 이진 파일을 찾는 것 외에도 GDBServer 통합은 symsrv를 통해 또는 로컬로 찾을 수 없는 경우 원격 파일 시스템으로부터 이러한 파일을 끌어옵니다. 일반적으로 symsrv 또는 로컬 검색 경로에서 기호를 가져오는 것보다 훨씬 느린 작업이지만 적절한 기호를 찾아 전반적인 환경을 개선합니다.

k 스택 명령을 사용하여 스택을 나열합니다. python3 모듈을 보여 주므로 WinDbg를 사용하여 Linux에서 python3을 디버깅하고 있음을 확인합니다.

0:000> k
 # Child-SP          RetAddr               Call Site
00 00007fff`ffffce10 00007fff`f786d515     libc_so!_select+0xbd
01 00007fff`ffffce80 00005555`55601ce8     readline_cpython_310_x86_64_linux_gnu!PyInit_readline+0xac5
02 00007fff`ffffcf60 00005555`556f06a1     python3!PyOS_Readline+0x109
03 00007fff`ffffcfa0 00005555`556eee7e     python3!PyFrame_LocalsToFast+0x62a1
04 00007fff`ffffd000 00005555`556edcf0     python3!PyFrame_LocalsToFast+0x4a7e
05 00007fff`ffffdb80 00005555`557a18e9     python3!PyFrame_LocalsToFast+0x38f0
06 00007fff`ffffdc00 00005555`557a1470     python3!PyCodec_LookupError+0xb09
07 00007fff`ffffdc50 00005555`557b89dc     python3!PyCodec_LookupError+0x690
08 00007fff`ffffdc70 00005555`5560b42f     python3!PyUnicode_Tailmatch+0xc6c
09 00007fff`ffffdcb0 00005555`5560b012     python3!PyRun_InteractiveLoopObject+0x4e0
0a 00007fff`ffffdd50 00005555`557b7678     python3!PyRun_InteractiveLoopObject+0xc3
0b 00007fff`ffffdda0 00005555`555f55c8     python3!PyRun_AnyFileObject+0x68
0c 00007fff`ffffddd0 00005555`555ea6e8     python3!PyRun_AnyFileExFlags+0x4f
0d 00007fff`ffffde00 00005555`55780cad     python3!Py_str_to_int+0x2342a
0e 00007fff`ffffdef0 00007fff`f7c7cd90     python3!Py_BytesMain+0x2d
0f 00007fff`ffffdf20 00007fff`f7c7ce40     libc_so!_libc_init_first+0x90
10 00007fff`ffffdfc0 00005555`55780ba5     libc_so!_libc_start_main+0x80
11 00007fff`ffffe010 ffffffff`ffffffff     python3!start+0x25
12 00007fff`ffffe018 00000000`00000000     0xffffffff`ffffffff

이 시점에서 원격 프로세스 서버를 통해 원격 Windows 디버거에 연결된 WinDbg를 사용하여 수행할 수 있는 거의 모든 작업을 수행할 수 있어야 합니다. 단계별, 원본 수준 디버그, 중단점 설정, 로컬 검사 등을 수행할 수 있습니다.

디버깅이 완료되면 Ctrl+D를 사용하여 WSL에서 gbdserver 창을 종료합니다.

프로세스 서버에 커넥트

사용자 모드 GDBServer를 통해 단일 프로세스에 연결하는 것 외에도 프로세스 서버로 설정하고 시스템의 기존 프로세스를 나열하고 연결할 수 있습니다. 이렇게 하려면 gdbserver가 "--multi" 명령줄 인수로 시작됩니다. gdbserver --multi localhost:1234

user1@USER1:/mnt/c/Users/USER1$ sudo gdbserver --multi localhost:1234
Listening on port 1234

프로세스 서버에 연결하려면 WinDbg에서 "서버 처리 파일/커넥트"을 선택하고 위의 단일 프로세스 gdbserver 예제와 동일한 프로토콜 문자열을 입력합니다.

gdb:server=localhost,port=1234

"확인" 단추를 클릭하면 gdbserver에 프로세스 서버로 연결되어야 합니다. dbgsrv와 마찬가지로 새 프로세스를 생성하거나 기존 프로세스를 나열하고 프로세스에 연결할 수 있습니다.

이 예제에서는 "프로세스에 연결" 옵션을 사용합니다.

20개 정도의 나열된 프로세스가 있는 프로세스에 대한 연결을 보여 주는 WinDbg 디버깅 시작 화면의 스크린샷

WINDOWS 프로세스(PID, 사용자 및 명령줄 포함)에 표시되는 것과 동일한 항목이 많이 표시됩니다. "프로세스에 연결" 대화 상자의 일부 열은 Linux와 관련이 없으며 데이터가 포함되지 않습니다.

세션 종료

Ctrl+D를 사용하여 WSL에서 gbdserver 창을 종료하고 WinDbg에서 디버깅 중지를 선택합니다. 세션을 종료하려면 경우에 따라 디버거를 종료해야 할 수 있습니다.

프로세스 서버에 다시 연결

WinDbg는 gdbserver가 프로세스에 연결되어 있는지 여부를 통해 "프로세스 서버" 및 "단일 대상"을 인식합니다. 일부 프로세스에 연결하고, 고정된 상태로 두고, 디버거를 닫고, 프로세스 서버에 다시 연결하려고 하면 프로세스 서버로 인식되지 않습니다. 이 경우 대상 gdbserver를 다시 시작하고 디버거를 다시 연결합니다.

Linux WinDbg 기능

코어 덤프(예: 스택 보행, 기호, 형식 정보, 지역 변수, 디스어셈블리 등)를 디버깅할 때는 디버거의 많은 기능이 예상대로 작동하지만, 전체 디버깅 도구 체인이 ELF, DWARF 및 Windows 의미 체계의 결과 차이를 아직 인식하지 못했다는 점에 유의해야 합니다. 디버거의 일부 명령은 현재 예기치 않은 출력을 초래할 수 있습니다. 예를 들어 lm ELF 모듈이 예상한 대로 잘못된 정보를 표시하고 PE 헤더를 수동으로 구문 분석합니다.

EXDI를 통한 Linux 커널 모드

Windows 디버거는 EXDI를 사용하여 커널 디버깅을 지원합니다. 이렇게 하면 다양한 하드웨어 및 운영 체제를 디버깅할 수 있습니다. EXDI 연결 구성 및 문제 해결에 대한 일반적인 내용은 EXDI 디버거 전송 구성을 참조 하세요.

EXDI를 사용하여 QEMU 커널 모드 디버깅을 설정하는 방법에 대한 자세한 내용은 EXDI를 사용하여 QEMU 커널 모드 디버깅 설정을 참조하세요.

Linux 기호 및 원본

이 섹션에서는 Linux 기호의 기본 사용 및 가용성에 대해 설명합니다. 자세한 내용은 Linux 기호 및 원본소스 코드 확장 액세스를 참조하세요.

DebugInfoD 기호 서버

WinDbg 버전 1.2104부터 원본 경로 명령(.srcpath, .lsrcpath(원본 경로 설정))은 태그를 통해 DebugInfoD* DebugInfoD 서버에서 파일 검색을 지원합니다.

태그는 DebugInfoD* 각 서버 URL이 형식이 https://domain.com 지정되고 로 구분 *된 하나 이상의 DebugInfoD 서버를 가리킬 수 있습니다. 서버는 원본 경로에 나열된 순서와 동일한 순서로 검색되고 파일은 일치하는 첫 번째 URL에서 검색됩니다. 자세한 내용은 소스 코드 확장 액세스를 참조 하세요.

예를 들어 .sympath(기호 경로 설정) 명령을 사용하여 다음과 같이 DebugInfoD 경로를 설정할 수 있습니다.

.sympath+ DebugInfoD*https://debuginfod.elfutils.org

기호 경로 설정에 대한 일반적인 내용은 기호 사용을 참조 하세요.

로드되는 기호에 대한 정보를 표시하려면 .를 사용합니다 !sym noisy. 자세한 내용은 !sym을 참조 하세요.

또한 해당 아티팩트 형식 반환을 지원하는 DebugInfoD 서버에서 원본의 자동 다운로드도 지원됩니다. 기본적으로 다음을 수행할 수 있습니다.

.srcpath+ DebugInfoD*https://debuginfod.elfutils.org

DWARF 기호 및 Linux 기호 유틸리티(예: !sourcemap!diesymLinux 기호) 작업에 대한 자세한 내용은 Linux 기호 및 원본을 참조 하세요.

C++ 앱 연습

  1. 텍스트 편집기(예: nano 또는 vi)를 사용하여 C++ 파일을 만듭니다. 예시:

nano DisplayGreeting.cpp

  1. 텍스트 편집기에서 C++ 프로그램을 작성합니다. 다음은 디버그해야 하는 인사말을 표시하는 간단한 프로그램입니다.
#include <array>
#include <cwchar>
#include <cstdio>
#include <iostream>
using namespace std;

void GetCppConGreeting(wchar_t* buffer, size_t size)
{
    wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!";
    wcsncpy(buffer, message, size);
}

int main()
{
    std::array<wchar_t, 50> greeting{};
    GetCppConGreeting(greeting.data(), greeting.size());

    cin.get();
    wprintf(L"%ls\n", greeting.data());

    return 0;
}
  1. Nano 편집기를 저장하고(Ctrl-O) 종료(Ctrl-X)합니다.

  2. g++를 사용하여 C++ 파일을 컴파일합니다. -o 옵션은 출력 파일 이름을 지정하는 데 사용되며 -g 옵션은 기호 파일을 생성합니다.

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. 코드에 오류가 없는 경우 g++ 명령은 디렉터리에 DisplayGreeting이라는 실행 파일을 만듭니다.

  2. 다음 명령을 사용하여 프로그램을 실행할 수 있습니다.

./DisplayGreeting

  1. 반환 키를 누르면 앱에 메시지가 표시됩니다. 출력을 보면 인사말이 잘리고 "????"이 대신 표시되는 것처럼 보입니다.

HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YO????

DisplayGreeting 디버깅

  1. 코드를 실행할 준비가 되면 gdbserver를 사용하여 앱을 시작할 수 있습니다.

gdbserver localhost:1234 DisplayGreeting

  1. WinDbg를 열고 "원격 디버거에 대한 파일/커넥트"을 선택하고 연결에 대한 프로토콜 문자열을 입력합니다. 이 예제에서는 다음 gdb:server=localhost,port=1234을 사용합니다.

  2. 연결되면 출력은 포트 1234에서 수신 대기 중이며 원격 디버깅 연결이 설정되었음을 나타내야 합니다.

Bob@Bob6:/mnt/c/Users/bob$ gdbserver localhost:1234 DisplayGreeting
Process /mnt/c/Users/bob/DisplayGreeting created; pid = 725
Listening on port 1234
Remote debugging from host 127.0.0.1, port 47700

이전에 멘션 일부 Linux 환경에서는 일반적으로 sudo를 사용하여 관리자 권한으로 명령을 실행해야 할 수 있습니다. 디버거 관리자 루트 수준 액세스를 사용하도록 설정하는 데 주의하고 필요한 경우에만 사용합니다.

디버거 세션에 원본 및 기호 경로 추가

중단점을 설정하고 소스 코드 및 변수를 보려면 기호 및 소스 경로를 설정합니다. 기호 경로 설정에 대한 일반적인 내용은 기호 사용을 참조 하세요.

디버거 세션에 기호 경로를 추가하는 데 사용합니다 .sympath . 이 예제에서 코드는 Bob이라는 사용자에 대해 WSL Linux Ubuntu의 이 위치에서 실행됩니다.

\\wsl$\Ubuntu\mnt\c\Users\Bob\

WSL에서 이 디렉터리가 다음의 Windows OS 위치에 매핑됩니다. C:\Users\Bob\

따라서 이 두 명령이 사용됩니다.

.sympath C:\Users\Bob\

.srcpath C:\Users\Bob\

WSL 파일 시스템에 대한 자세한 내용은 WSL에 대한 파일 권한을 참조하세요.

  1. 추가 Linux OS 기호를 활용하려면 다음과 같이 .sympath 위치를 사용하여 DebugInfoD 기호를 추가합니다.

.sympath+ DebugInfoD*https://debuginfod.elfutils.org

  1. 또한 해당 아티팩트 형식 반환을 지원하는 DebugInfoD 서버에서 원본의 자동 다운로드도 지원됩니다. 이를 활용하려면 .srcpath를 사용하여 elfutils 서버를 추가합니다.

.srcpath+ DebugInfoD*https://debuginfod.elfutils.org

중단점 설정

DisplayGreeting 앱의 기본 중단점을 설정합니다.

0:000> bp DisplayGreeting!main
0:000> bl
     0 e Disable Clear  00005555`55555225  [/mnt/c/Users/bob/DisplayGreeting.cpp @ 14]     0001 (0001)  0:**** DisplayGreeting!main

Go 명령 또는 메뉴 옵션을 사용하여 코드 실행을 다시 시작합니다.

소스 코드 로드

.reload 명령을 사용하여 기호를 다시 로드합니다.

lm 명령을 사용하여 DisplayGreeting 앱을 실행하고 있는지 확인합니다.

0:000> lm
start             end                 module name
00005555`55554000 00005555`55558140   DisplayGreeting T (service symbols: DWARF Private Symbols)        c:\users\bob\DisplayGreeting
00007fff`f7a54000 00007fff`f7a732e8   libgcc_s_so   (deferred)             
00007fff`f7a74000 00007fff`f7b5a108   libm_so    (deferred)             
00007fff`f7b5b000 00007fff`f7d82e50   libc_so  T (service symbols: DWARF Private Symbols)        C:\ProgramData\Dbg\sym\_.debug\elf-buildid-sym-a43bfc8428df6623cd498c9c0caeb91aec9be4f9\_.debug
00007fff`f7d83000 00007fff`f7fae8c0   libstdc___so   (deferred)             
00007fff`f7fc1000 00007fff`f7fc1000   linux_vdso_so   (deferred)             
00007fff`f7fc3000 00007fff`f7ffe2d8   ld_linux_x86_64_so T (service symbols: DWARF Private Symbols)        C:\ProgramData\Dbg\sym\_.debug\elf-buildid-sym-9718d3757f00d2366056830aae09698dbd35e32c\_.debug

명령이 표시 인사말 코드에 대한 액세스를 트리거하면 WinDbg에 표시됩니다.

줄 19, wprint에 중단점이 설정된 WinDbg의 DisplayGreeting.cpp 코드 스크린샷

'k' 명령을 사용하여 스택을 나열합니다.

0:000> k
 # Child-SP          RetAddr               Call Site
00 00007fff`ffffde00 00007fff`f7b84d90     DisplayGreeting!main+0x1f [/mnt/c/Users/BOB/DisplayGreeting.cpp @ 15] 
01 00007fff`ffffdef0 00007fff`f7b84e40     libc_so!__libc_start_call_main+0x80 [./csu/../sysdeps/x86/libc-start.c @ 58] 
02 00007fff`ffffdf90 00005555`55555125     libc_so!__libc_start_main_impl+0x80 [./csu/../sysdeps/nptl/libc_start_call_main.h @ 379] 
03 00007fff`ffffdfe0 ffffffff`ffffffff     DisplayGreeting!start+0x25
04 00007fff`ffffdfe8 00000000`00000000     0xffffffff`ffffffff```

dx 명령을 사용하여 지역 변수 인사말을 봅니다. 크기는 50입니다.

0:000> dx greeting
greeting                 : { size=50 } [Type: std::array<wchar_t, 50>]
    [<Raw View>]     [Type: std::array<wchar_t, 50>]

코드를 살펴보고 50은 인사말 메시지에 충분한 크기가 아닐 수 있습니다.

wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL

인사말에 대한 지역 변수를 확장하고 인사말이 잘리는 것을 확인하여 이를 확인합니다.

gdbserver 연결 문제 해결

--debug gdbserver 콘솔에 추가 정보를 표시하여 연결 상태 대한 자세한 정보를 수집하는 옵션을 사용합니다. 예를 들어 프로세스 서버를 시작하려면 이 명령을 사용합니다.

gdbserver --debug --multi localhost:1234

참고 항목

Linux 기호 및 원본

소스 코드 확장 액세스

ELFUTILS 디버긴포드

최상의 원격 디버깅 방법 선택