Condividi tramite


Debug di processi remoti live linux

Questo articolo descrive come stabilire una connessione WinDbg dinamica a Linux. Il debug di processi remoti live in Linux richiede WinDbg versione 1.2402.24001.0 o successiva.

Il debugger GNU - GDBServer viene usato in Linux per supportare la connessione WinDbg. Per altre informazioni su GDBServer, vedere https://en.wikipedia.org/wiki/Gdbserver. Una posizione in cui visualizzare la documentazione per il debug remoto di gdb è disponibile qui: https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

Gli esempi qui usano il sottosistema Windows per Linux (WSL), ma è anche possibile usare altre implementazioni di Linux.

Tipi WinDbg di debug di processi remoti

Esistono due metodi principali per eseguire il debug remoto con WinDbg: un server di elaborazione o un server di connessione KD. I server di elaborazione vengono usati per il debug in modalità utente; I server di connessione KD vengono usati per il debug in modalità kernel. Per informazioni generali su questi tipi di connessione WinDbg, vedere Server di elaborazione (modalità utente) e server di connessione KD (modalità kernel).

Esistono due modi per avviare il debug dei processi in modalità utente Linux. È possibile avviare gdbserver in un determinato processo oppure avviare gdbserver come server di elaborazione in grado di elencare e collegare i processi esistenti. Questo è molto simile al server di elaborazione DbgSrv (dbgsrv.exe) in Windows. Per altre informazioni, vedere Attivazione di un server di elaborazione.

Debug del processo Linux in modalità utente

È possibile connettersi a un processo specifico in modalità utente singolo o in modalità multipla per visualizzare tutto il processo in un elenco e selezionarlo per connettersi. Entrambi i metodi sono descritti in questo argomento. Entrambi i metodi condividono la stessa sintassi stringa di connessione, descritta di seguito.

Formato per gdbserver stringa di connessione

Il formato usato per connettersi a gdbserver è "protocol:arguments", dove gli argomenti sono un elenco delimitato da virgole di "argument=value". Per una connessione gdbserver in modalità utente, il protocollo è gdb e il set di argomenti è il seguente.

server=<address> - Obbligatorio: indica l'indirizzo IP del server gdb a cui connettersi.

port=<port> - Obbligatorio: indica il numero di porta del server gdb a cui connettersi.

threadEvents=<true|false> - Facoltativo: indica se gli eventi del thread per questa versione di gdbserver funzionano correttamente in modalità di arresto.

Si è verificato un problema nelle versioni correnti di gdbserver, in cui l'abilitazione di eventi di thread con un server in modalità di arresto (che winDbg usa) causerà l'arresto anomalo di gdbserver. Se questo valore è false (il valore predefinito), gli eventi di avvio e arresto del thread verranno sintetizzati ma potrebbero apparire significativamente più tardi del tempo effettivo di creazione/distruzione del thread. Quando è disponibile una correzione in gdbserver, gli eventi effettivi possono essere abilitati tramite questa opzione.

Connessione a un processo in modalità utente singolo

Questa sezione descrive come identificare e connettersi a un singolo processo in modalità utente in Linux usando WinDbg.

WSL (Sottosistema di Windows per Linux)

Gli esempi qui usano WSL (sottosistema Windows per Linux), ma è possibile usare altre implementazioni di Linux. Per informazioni sulla configurazione e sull'uso di WSL, vedere:

Selezionare il processo desiderato

Elencare i processi in Linux usando il ps -A comando per determinare il processo in esecuzione a cui connettersi.

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

In questa procedura dettagliata di esempio ci si connetterà a python3.

Individuare l'indirizzo IP del sistema di destinazione

Se ci si connette a una destinazione Linux remota, usare un comando come ip route showper determinare l'indirizzo IP esterno.

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

In questa procedura dettagliata ci connetteremo a WSL in esecuzione nello stesso PC e useremo l'indirizzo IP di localhost.

Collegare GDBServer al processo selezionato

Nella console WSL linux immettere gdbserver localhost:1234 python3 per avviare gdbserver sulla porta 1234 e collegarlo al processo python3.

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

Per alcuni ambienti Linux, potrebbe essere necessario eseguire il comando come amministratore, ad esempio usando sudo - sudo gdbserver localhost:1234 python3. Prestare attenzione con l'abilitazione dell'accesso al livello radice dell'amministratore del debugger e usarlo solo quando è necessario.

Creare la connessione al server di elaborazione in WinDbg

Aprire WinDbg e selezionare "File/Connetti al debugger remoto" e immettere una stringa di protocollo per la connessione. Per questo esempio si userà : gdb:server=localhost,port=1234.

screenshot della schermata Start debugging di WinDbg che mostra stringa di connessione.

Dopo aver fatto clic sul pulsante OK, il debugger dovrebbe connettersi al gdbserver e dovrebbe trovarsi all'interruzione iniziale del processo.

Una volta che si è nel punto di interruzione iniziale, è possibile premere "g" più volte. Si otterranno messaggi di caricamento del modulo (e gli eventi "interruzione al caricamento del modulo" dovrebbero funzionare correttamente).

Si noti che potrebbero essere necessari alcuni minuti per arrivare a quel punto, perché i simboli di debug vengono caricati nella cache. Oltre a cercare simboli e file binari tramite il server dei simboli o il percorso di ricerca locale, l'integrazione di GDBServer ha la possibilità di eseguire il pull di questi file dal file system remoto se non possono essere trovati tramite symsrv o localmente. Si tratta in genere di un'operazione molto più lenta rispetto all'acquisizione di simboli da symsrv o da un percorso di ricerca locale, ma rende l'esperienza complessiva migliore individuando i simboli appropriati.

Usare il comando k stacks per elencare lo stack. Mostra i moduli python3, quindi conferma che si sta eseguendo il debug di Python3 in Linux usando 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

A questo punto, dovresti essere in grado di eseguire quasi tutte le operazioni che possono essere eseguite usando WinDbg collegato a un debugger Windows remoto su un server di elaborazione remoto. È possibile eseguire passaggi, debug a livello di origine, impostare punti di interruzione, esaminare le variabili locali e così via.

Al termine del debug, usare CTRL+D per uscire dalla finestra gbdserver in WSL.

Connessione a un server di elaborazione

Oltre a connettersi a un singolo processo tramite un GDBServer in modalità utente, è possibile impostarne uno come server di elaborazione ed elencare e collegarsi ai processi esistenti nel sistema. A tale scopo, gdbserver viene avviato con l'argomento della riga di comando "--multi" - gdbserver --multi localhost:1234

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

Per connettersi al server di elaborazione, selezionare "File/Connetti al server di elaborazione" in WinDbg e immettere la stessa stringa di protocollo dell'esempio gdbserver del processo singolo precedente:

gdb:server=localhost,port=1234

Dopo aver fatto clic sul pulsante "OK", si dovrebbe essere connessi al server di elaborazione gdbserver come server di elaborazione. Come per dbgsrv, è possibile generare un nuovo processo oppure elencare i processi esistenti e collegarsi a uno.

In questo esempio usare l'opzione "Connetti a processo".

screenshot della schermata Avvia debug di WinDbg che mostra il collegamento al processo con 20 processi elencati.

Si noti che verranno visualizzati molti degli stessi elementi visibili per i processi di Windows (inclusi PID, utente e riga di comando). Alcune colonne della finestra di dialogo "Collega a processo" non sono rilevanti per Linux e non conterranno dati.

Fine della sessione

Usare CTRL+D per uscire dalla finestra gbdserver in WSL e selezionare Arresta debug in WinDbg. Per terminare la sessione, in alcuni casi potrebbe essere necessario uscire dal debugger.

Riconnessione al server di elaborazione

WinDbg riconosce un "server di elaborazione" rispetto a una "singola destinazione" tramite se il gdbserver è collegato a un processo o meno. Se si esegue il collegamento a un processo, lasciarlo bloccato, chiudere il debugger e provare a riconnettersi al server di elaborazione, in tutta probabilità, non verrà riconosciuto come server di elaborazione. In questo caso, riavviare il server gdbserver di destinazione e riconnettere il debugger.

Funzionalità WinDbg di Linux

Anche se gran parte delle funzionalità del debugger funzionerà come previsto" nel debug dei dump di base (ad esempio, il passaggio dello stack, i simboli, le informazioni sui tipi, le variabili locali, il disassembly e così via...) è importante notare che l'intera toolchain di debug non è stata ancora resa nota di ELF, NANO e le differenze risultanti dalla semantica di Windows. Alcuni comandi nel debugger potrebbero attualmente comportare un output imprevisto. Ad esempio, lm visualizzerà informazioni non corrette per un modulo ELF come previsto e analizza manualmente le intestazioni PE.

Modalità kernel Linux tramite EXDI

Il debugger di Windows supporta il debug del kernel tramite EXDI. In questo modo è possibile eseguire il debug di un'ampia gamma di hardware e sistemi operativi. Per informazioni generali sulla configurazione e sulla risoluzione dei problemi relativi alle connessioni EXDI, vedere Configurazione del trasporto del debugger EXDI.

Per informazioni su come configurare il debug in modalità kernel QEMU con EXDI, vedere Configurazione del debug in modalità kernel QEMU con EXDI.

Simboli e origini Linux

Questa sezione descrive l'uso di base e la disponibilità dei simboli Linux. Per informazioni più dettagliate, vedere Simboli e origini Linux e accesso esteso al codice sorgente.

Server di simboli DebugInfoD

A partire da WinDbg versione 1.2104, il comando percorso di origine (.srcpath, .lsrcpath (Set Source Path)) supporta il recupero di file dai server DebugInfoD tramite il DebugInfoD* tag .

Il DebugInfoD* tag può puntare a uno o più server DebugInfoD con ogni URL del server formattato https://domain.com come e separato da *. I server verranno cercati nello stesso ordine indicato nel percorso di origine e i file verranno recuperati dal primo URL corrispondente. Per altre informazioni, vedere Accesso esteso al codice sorgente.

Ad esempio, è possibile usare il comando .sympath (Set Symbol Path) per impostare un percorso DebugInfoD simile al seguente.

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

Per informazioni generali sull'impostazione del percorso dei simboli, vedere Uso dei simboli.

Per visualizzare informazioni sui simboli da caricare, usare !sym noisy. Per altre informazioni, vedere !sym.

È supportato anche il download automatico delle origini dai server DebugInfoD che supportano la restituzione di tale tipo di artefatto. È possibile, in sostanza, eseguire:

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

Per altre informazioni sull'uso dei simboli NANO e delle utilità dei simboli Linux, ad esempio !sourcemap e !diesym, vedere Simboli e origini Linux.

Procedura dettagliata per l'app C++

  1. Usare un editor di testo (ad esempio nano o vi) per creare il file C++. Ad esempio:

nano DisplayGreeting.cpp

  1. Nell'editor di testo scrivere il programma C++. Ecco un semplice programma che visualizza i messaggi di saluto, che deve essere sottoposto a debug:
#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. Salvare (CTRL-O) e chiudere (CTRL-X) l'editor nano.

  2. Compilare il file C++ usando g++. L'opzione -o viene usata per specificare il nome del file di output e l'opzione -g genera un file di simboli:

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. Se non sono presenti errori nel codice, il comando g++ creerà un file eseguibile denominato DisplayGreeting nella directory.

  2. È possibile eseguire il programma usando il comando seguente:

./DisplayGreeting

  1. Premendo il tasto restituito viene visualizzato il messaggio nell'app. Esaminando l'output, sembra che il messaggio di saluto venga troncato e che venga visualizzato "????".

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

Debug di DisplayGreeting

  1. Quando il codice è pronto per l'esecuzione, è possibile avviare l'app usando gdbserver.

gdbserver localhost:1234 DisplayGreeting

  1. Aprire WinDbg e selezionare "File/Connetti al debugger remoto" e immettere una stringa di protocollo per la connessione. Per questo esempio si userà : gdb:server=localhost,port=1234.

  2. Una volta connesso, l'output dovrebbe indicare che è in ascolto sulla porta 1234 e che viene stabilita la connessione di debug remoto.

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

Come accennato in precedenza, per alcuni ambienti Linux potrebbe essere necessario eseguire il comando come amministratore, in genere usando sudo. Prestare attenzione con l'abilitazione dell'accesso al livello radice dell'amministratore del debugger e usarlo solo quando è necessario.

Aggiungere i percorsi di origine e simboli alla sessione del debugger

Per impostare i punti di interruzione e visualizzare il codice sorgente e le variabili, impostare i simboli e il percorso di origine. Per informazioni generali sull'impostazione del percorso dei simboli, vedere Uso dei simboli.

Usare .sympath per aggiungere il percorso del simbolo alla sessione del debugger. In questo esempio il codice è in esecuzione in questo percorso in WSL Linux Ubuntu per un utente di nome Bob.

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

In WSL questa directory esegue il mapping alla posizione del sistema operativo Windows di: C:\Users\Bob\

Questi due comandi vengono quindi usati.

.sympath C:\Users\Bob\

.srcpath C:\Users\Bob\

Per altre informazioni sul file system WSL, vedere Autorizzazioni file per WSL.

  1. Per trarre vantaggio da altri simboli del sistema operativo Linux, aggiungere i simboli DebugInfoD usando il percorso con estensione sympath, come illustrato di seguito.

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

  1. È supportato anche il download automatico delle origini dai server DebugInfoD che supportano la restituzione di tale tipo di artefatto. Per sfruttare questo vantaggio, aggiungere il server elfutils usando .srcpath.

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

Imposta punto di interruzione

Impostare un punto di interruzione nella parte principale dell'app 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

Usare l'opzione di menu o comando Go per riavviare l'esecuzione del codice.

Caricamento del codice sorgente

Usare il comando .reload per ricaricare i simboli.

Usare il lm comando per verificare che sia in esecuzione l'app 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

Quando il comando attiva l'accesso al codice di saluto visualizzato, verrà visualizzato in WinDbg.

screenshot del codice DisplayGreeting.cpp in WinDbg con punto di interruzione impostato sulla riga 19, wprint

Usare il comando 'k' per elencare lo stack.

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```

Usare il comando dx per visualizzare il messaggio di saluto della variabile locale. Prendere nota che le dimensioni sono 50.

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

Esaminare il codice e notare che la dimensione 50 potrebbe non essere sufficiente per il messaggio di saluto.

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

Confermare questa operazione espandendo la variabile variabili locali per il messaggio di saluto e vedendo che il messaggio di saluto viene troncato.

Risoluzione dei problemi relativi alla connessione gdbserver

Usare l'opzione --debug per visualizzare informazioni aggiuntive nella console gdbserver per raccogliere altre informazioni sullo stato della connessione. Ad esempio, per avviare un server di elaborazione, usare questo comando.

gdbserver --debug --multi localhost:1234

Vedi anche

Simboli e origini Linux

Accesso esteso al codice sorgente

Dump di arresto anomalo di Linux

ELFUTILS debuginfod

Scelta del metodo di debug remoto migliore