Sdílet prostřednictvím


Ladění aktivních vzdálených procesů v Linuxu

Tento článek popisuje, jak vytvořit živé připojení WinDbg k Linuxu. Ladění živého vzdáleného procesu v Linuxu vyžaduje WinDbg verze 1.2402.24001.0 nebo vyšší.

Gnu Debugger - GDBServer, se používá v Linuxu k podpoře připojení WinDbg. Další informace o GDBServer naleznete v tématu https://en.wikipedia.org/wiki/Gdbserver. Tady je jedno místo pro zobrazení dokumentace pro vzdálené ladění gdb – https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

Zde uvedené příklady používají subsystém Windows pro Linux (WSL), ale lze použít i jiné implementace Linuxu.

Typy ladění vzdáleného procesu pomocí WinDbg

Existují dvě primární metody vzdáleného ladění pomocí WinDbg – procesový server nebo server připojení KD. Procesové servery se používají pro ladění v uživatelském režimu; Servery připojení KD se používají k ladění v režimu jádra. Obecné informace o těchto typech připojení WinDbg najdete v části Procesové servery (uživatelský režim) a servery připojení KD (režim jádra ).

Existují dva způsoby, jak začít debugovat procesy v uživatelském režimu Linuxu. Můžete buď spustit gdbserver na konkrétním procesu, nebo spustit gdbserver jako procesový server, který může zobrazit seznam existujících procesů a připojit se k nim. Je to podobně jako procesový server DbgSrv (dbgsrv.exe) ve Windows. Další informace naleznete v tématu Aktivace procesového serveru.

Ladění procesu Linuxu v uživatelském režimu

Můžete se připojit ke konkrétnímu procesu uživatelského režimu nebo v režimu s více režimy, abyste viděli celý proces v seznamu a vybrali ho, ke kterému se chcete připojit. Obě metody jsou popsány v tomto tématu. Obě metody sdílejí stejnou syntaxi připojovacího řetězce, jak je popsáno dále.

Formát připojovacího řetězce gdbserveru

Formát použitý pro připojení k gdbserveru je "protocol:arguments", kde argumenty jsou čárkami oddělený seznam argumentu argument=value. Pro připojení gdbserver uživatelského režimu je protokol gdb a sada argumentů je následující.

server=<address> – Povinné: označuje IP adresu serveru gdb, ke kterému se má připojit.

port=<port> – Povinný argument: označuje číslo portu serveru gdb, ke kterému se má připojit.

threadEvents=<true|false> - Volitelné: Označuje, zda události vlákna pro tuto verzi gdbserver fungují správně v režimu zastavení.

V aktuálních verzích gdbserveru došlo k problému, kdy povolení událostí vlákna se serverem v režimu zastavení (který používá WinDbg) způsobí chybové ukončení gdbserveru. Pokud je tato hodnota false (výchozí hodnota), události spuštění a zastavení vlákna budou syntetizovány, ale mohou se objevit výrazně později než skutečný čas vytvoření nebo zničení vlákna. Pokud je v gdbserveru k dispozici oprava, je možné prostřednictvím této možnosti povolit skutečné události.

Připojení k procesu v režimu pro jednoho uživatele

Tato část popisuje, jak identifikovat a připojit se k jednomu procesu uživatelského režimu v Linuxu pomocí WinDbg.

WSL (subsystém Windows pro Linux)

Zde uvedené příklady používají WSL (Subsystém Windows pro Linux), ale je možné použít i jiné implementace Linuxu. Informace o nastavení a používání WSL najdete v tématech:

Výběr požadovaného procesu

Vytvořte seznam procesů v Linuxu ps -A pomocí příkazu k určení spuštěného procesu, ke kterému se chcete připojit.

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

V tomto příkladu se připojíme k Python3.

Vyhledání IP adresy cílového systému

Pokud se připojujete ke vzdálenému cíli Linuxu, použijte příkaz, například ip route showk určení externí IP adresy.

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

V tomto názorném postupu se připojíme ke službě WSL spuštěné na stejném počítači a použijeme IP adresu místního hostitele.

Připojení GDBServer k vybranému procesu

V konzole WSL linuxu zadejte gdbserver localhost:1234 python3 , abyste spustili gdbserver na portu 1234 a připojili ho k procesu Python3.

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

V některých linuxových prostředích může být příkaz potřeba spustit jako správce, například pomocí sudo - sudo gdbserver localhost:1234 python3. Při povolování přístupu ladicího programu na úrovni správce root buďte opatrní a používejte jej jenom v případě potřeby.

Vytvoření připojení procesového serveru ve WinDbg

Otevřete WinDbg a vyberte "File / Connect to Remote Debugger" (Soubor / Připojit ke vzdálenému ladicím programu) a zadejte řetězec protokolu pro připojení. V tomto příkladu použijeme: gdb:server=localhost,port=1234.

Snímek obrazovky s obrazovkou Start při ladění WinDbg s připojovacím řetězcem

Po kliknutí na tlačítko OK by se ladicí program měl připojit ke gdbserveru a měl byste být na počátečním bodě přerušení procesu.

Jakmile budete na počáteční zarážce, můžete opakovaně stisknout "g". Zobrazí se zprávy o načtení modulu (a události ve stylu sxe „přerušit při načtení modulu“ by měly správně fungovat).

Mějte na paměti, že může chvíli trvat, než se dostanete k tomu bodu, protože symboly ladění se načítají do mezipaměti. Kromě hledání symbolů a binárních souborů prostřednictvím serveru symbolů nebo místní cesty hledání má integrace GDBServer možnost načíst tyto soubory ze vzdáleného systému souborů, pokud je nelze najít přes symsrv nebo místně. Obvykle je to mnohem pomalejší operace než získání symbolů z symsrv nebo místní cesty hledání, ale celkový zážitek je lepší tím, že vyhledá příslušné symboly.

K výpisu zásobníku použijte příkaz k stacks. Zobrazuje moduly python3, což potvrzuje, že ladíme python3 na Linuxu pomocí WinDbg.

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

V tomto okamžiku byste měli být schopni provádět téměř vše, co lze provést pomocí WinDbg připojeného k ladicímu programu Windows přes vzdálený procesový server. Můžete přejít krok za krokem, ladit na úrovni zdroje, nastavit body přerušení, kontrolovat místní hodnoty atd.

Po dokončení ladění ukončete okno gbdserveru ve WSL pomocí kombinace kláves CTRL+D.

Připojení k procesovém serveru

Kromě připojení k jednomu procesu prostřednictvím uživatelského režimu GDBServeru můžete GDBServer nastavit jako procesový server a vypsat a připojit se k existujícím procesům v systému. K tomu se spustí gdbserver s argumentem příkazového řádku --multi – gdbserver --multi localhost:1234

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

Pokud se chcete připojit k procesovém serveru, vyberte v WinDbg "File / Connect to process server" (Soubor / Připojit k procesovém serveru) a zadejte stejný řetězec protokolu jako v příkladu gdbserver s jedním procesem výše:

gdb:server=localhost,port=1234

Jakmile kliknete na tlačítko OK, měli byste být připojeni k serveru gdb jako procesový server. Stejně jako u dbgsrv můžete vytvořit nový proces, nebo můžete vypsat existující procesy a připojit se k němu.

V tomto příkladu použijte možnost Připojit k procesu.

Snímek obrazovky z úvodní obrazovky ladění WinDbg, zobrazující připojení k procesu a přibližně 20 uvedených procesů.

Všimněte si, že uvidíte mnoho stejných věcí, které jsou viditelné pro procesy Windows (včetně PID, uživatele a příkazového řádku). Některé sloupce v dialogovém okně Připojit k procesu nejsou pro Linux relevantní a nebudou obsahovat data.

Ukončení relace

Pomocí CTRL+D ukončete okno gdbserveru ve WSL a vyberte zastavení ladění ve WinDbg. K ukončení relace může být v některých případech nutné odejít z ladicího programu.

Opětovné připojení k procesového serveru

WinDbg rozpozná "procesový server" a "jeden cíl" prostřednictvím toho, jestli je gdbserver připojený k procesu, nebo ne. Pokud se připojíte k určitému procesu, necháte ho zablokovaný, zavřete ladicí program a pokusíte se znovu připojit k procesnímu serveru, je pravděpodobné, že ho nerozpoznáme jako procesní server. V takovém případě restartujte cílový gdbserver a znovu připojte ladicí program.

Funkce WinDbg pro Linux

I když většina funkcí ladicího programu bude fungovat podle očekávání při ladění základních výpisů paměti (např. procházení zásobníku, symboly, informace o typech, místní proměnné, dekompilace atd.), je důležité si uvědomit, že celá sada nástrojů ladění ještě není plně obeznámena s ELF, DWARF a výslednými rozdíly od sémantiky Windows. Některé příkazy v ladicím programu můžou v současné době vést k neočekávanému výstupu. lm stále zobrazí nesprávné informace pro modul ELF, protože očekává a ručně parsuje hlavičky PE.

Režim jádra Linuxu přes EXDI

Ladicí program systému Windows podporuje ladění jádra pomocí EXDI. To umožňuje ladění široké škály hardwaru a operačních systémů. Obecné informace o nastavení konfigurace a řešení potíží s připojením EXDI naleznete v tématu Konfigurace přenosu ladicího programu EXDI.

Informace o tom, jak nastavit ladění QEMU Kernel-Mode pomocí EXDI, naleznete v tématu Nastavení QEMU Kernel-Mode ladění pomocí EXDI.

Symboly a zdroje Linuxu

Tato část popisuje základní použití a dostupnost symbolů Linuxu. Podrobnější informace najdete v tématu Symboly a zdroje Linuxu a Rozšířený přístup ke zdrojovému kódu.

Servery symbolů DebugInfoD

Od verze WinDbg 1.2104 příkaz zdrojové cesty (.srcpath, .lsrcpath (Nastavit zdrojovou cestu)) podporuje načítání souborů ze serverů DebugInfoD prostřednictvím značky DebugInfoD*.

Značka DebugInfoD* může odkazovat na jeden nebo více serverů DebugInfoD, přičemž URL každého serveru je formátována jako https://domain.com a oddělená *. Servery budou prohledány ve stejném pořadí jako ve zdrojové cestě a soubory se načtou z první odpovídající adresy URL. Další informace naleznete v části Rozšířený přístup ke zdrojovému kódu.

Můžete například použít příkaz .sympath (Set Symbol Path) k nastavení cesty DebugInfoD takto.

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

Obecné informace o nastavení cesty symbolů naleznete v tématu Použití symbolů.

Chcete-li zobrazit informace o načtených symbolech, použijte !sym noisy. Další informace naleznete v tématu !sym.

Podporuje se také automatické stahování zdrojů ze serverů DebugInfoD, které podporují vrácení tohoto typu artefaktu. V podstatě můžete:

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

Další informace o práci se symboly DWARF a linuxovými nástroji pro práci se symboly, například !sourcemap a !diesym, viz Symboly a zdroje Linuxu.

Návod k aplikaci C++

  1. K vytvoření souboru C++ použijte textový editor (například nano nebo vi). Například:

nano DisplayGreeting.cpp

  1. V textovém editoru napište program C++. Tady je jednoduchý program, který zobrazuje pozdravy, který je potřeba ladit:
#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. Uložte (CTRL-O) a ukončete editor nano (CTRL-X).

  2. Zkompilujte soubor C++ pomocí g++. Možnost -o slouží k zadání názvu výstupního souboru a možnost -g vygeneruje soubor symbolů:

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. Pokud kód neobsahuje žádné chyby, příkaz g++ vytvoří ve vašem adresáři spustitelný soubor s názvem DisplayGreeting.

  2. Program můžete spustit pomocí následujícího příkazu:

./DisplayGreeting

  1. Stisknutím návratové klávesy se zobrazí zpráva v aplikaci. Když se podíváte na výstup, vypadá to, že se zkracuje pozdrav a místo toho se zobrazuje "????".

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

Ladění sady DisplayGreeting

  1. Jakmile je kód připravený ke spuštění, můžeme aplikaci spustit pomocí gdbserveru.

gdbserver localhost:1234 DisplayGreeting

  1. Otevřete WinDbg a vyberte "File / Connect to Remote Debugger" (Soubor / Připojit ke vzdálenému ladicím programu) a zadejte řetězec protokolu pro připojení. V tomto příkladu použijeme: gdb:server=localhost,port=1234.

  2. Po připojení výstupu by mělo být uvedeno, že naslouchá na portu 1234 a že je navázáno vzdálené ladění připojení.

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

Jak už jsme zmínili dříve, v některých linuxových prostředích může být příkaz potřeba spustit jako správce, obvykle pomocí příkazu sudo. Při povolování přístupu ladicího programu na úrovni správce root buďte opatrní a používejte jej jenom v případě potřeby.

Přidejte cesty ke zdroji a symbolům do relace ladicího programu

Pokud chcete nastavit zarážky a zobrazit zdrojový kód a proměnné, nastavte symboly a zdrojovou cestu. Obecné informace o nastavení cesty symbolů naleznete v tématu Použití symbolů.

Použijte .sympath k přidání cesty symbolu do relace ladicího programu. V tomto příkladu kód běží na tomto místě ve WSL Linux Ubuntu pro uživatele jménem Bob.

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

Ve WSL se tento adresář mapuje na umístění operačního systému Windows: C:\Users\Bob\

Tyto dva příkazy se tedy používají.

.sympath C:\Users\Bob\

.srcpath C:\Users\Bob\

Další informace o systému souborů WSL naleznete v tématu Oprávnění k souborům wsL.

  1. Pokud chcete využívat další symboly operačního systému Linux, přidejte symboly DebugInfoD pomocí cesty .sympath, například takto.

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

  1. Podporuje se také automatické stahování zdrojů ze serverů DebugInfoD, které podporují vrácení tohoto typu artefaktu. Pokud to chcete využít, přidejte server elfutils pomocí cesty .srcpath.

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

Nastavte zarážku

Nastavte bod přerušení v hlavní funkci aplikace 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

K obnovení běhu kódu použijte příkaz Go nebo možnost nabídky.

Načítání zdrojového kódu

K opětovnému načtení symbolů použijte příkaz .reload.

lm Pomocí příkazu potvrďte, že používáme aplikaci 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

Jakmile příkaz aktivuje přístup k kódu pro zobrazení pozdravu, zobrazí se v WinDbg.

snímek obrazovky kódu DisplayGreeting.cpp v prostředí WinDbg s přerušením nastaveným na řádce 19, příkaz wprint

Pomocí příkazu 'k' zobrazte zásobní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```

Pomocí příkazu dx zobrazte místní proměnnou greeting. Mějte na paměti, že jeho velikost je 50.

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

Prohlédněte si kód a všimněte si, že hodnota 50 nemusí být dostatečná pro zprávu s pozdravem.

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

Potvrďte to tak, že rozbalíte proměnnou locals obsahující pozdrav a uvidíte, že je pozdrav zkrácený.

Řešení potíží s připojením gdbserveru

--debug Pomocí možnosti zobrazíte další informace v konzole gdbserver ke shromáždění dalších informací o stavu připojení. Například ke spuštění procesového serveru použijte tento příkaz.

gdbserver --debug --multi localhost:1234

Viz také

symboly a zdroje Linuxu

Rozšířený přístup ke zdrojovému kódu

výpisy paměti při pádu systému Linux

ELFUTILS debuginfod

volba nejlepší metody vzdáleného ladění