Compartir a través de


Depuración de procesos remotos en vivo de Linux

En este artículo se describe cómo establecer una conexión en vivo de WinDbg a Linux. La depuración de procesos remotos en vivo en Linux requiere WinDbg versión 1.2402.24001.0 o superior.

El depurador GNU - GDBServer se usa en Linux para permitir la conexión WinDbg. Para más información sobre GDBServer, consulte https://en.wikipedia.org/wiki/Gdbserver. Aquí puede consultar documentación de la depuración remota de gdb: https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

En los ejemplos se usa el subsistema Windows para Linux (WSL), pero también se pueden usar otras implementaciones de Linux.

Tipos WinDbg de depuración de procesos remotos

Hay dos métodos principales para realizar la depuración remota con WinDbg: un servidor de procesos o un servidor de conexión KD. Los servidores de procesos se usan para la depuración en modo de usuario; los servidores de conexión KD se usan para la depuración en modo kernel. Consulte Servidores de procesos (modo de usuario) y Servidores de conexión KD (modo kernel) para obtener información general sobre estos tipos de conexión WinDbg.

Hay dos maneras de iniciar la depuración de procesos de modo de usuario de Linux. Puede iniciar gdbserver en un proceso determinado o puede iniciar gdbserver como un servidor de procesos que pueda enumerar y adjuntar a los procesos existentes. Esto es muy similar al servidor de procesos DbgSrv (dbgsrv.exe) en Windows. Para obtener más información, consulte Activación de un servidor de procesos.

Depuración de procesos en modo de usuario de Linux

Es posible conectarse a un proceso de modo de usuario único específico, o en modo múltiple, para ver todo el proceso en una lista y seleccionar uno para conectarse. Ambos métodos se describen en este tema. Ambos métodos comparten la misma sintaxis de cadena de conexión, que se describe a continuación.

Formato de la cadena de conexión gdbserver

El formato usado para conectarse a gdbserver es "protocol:arguments", donde los argumentos son una lista separada por comas de "argument=value". Para una conexión gdbserver en modo de usuario, el protocolo es gdb y el conjunto de argumentos es el siguiente.

server=<address> - Obligatorio: indica la dirección IP del gdbserver a la que conectarse.

port=<port> - Obligatorio: indica el número de puerto del gdbserver al que conectarse.

threadEvents=<true|false> - Opcional: indica si los eventos de subproceso de esta versión de gdbserver funcionan correctamente en modo de detención.

Hay un problema en las versiones actuales de gdbserver, donde la habilitación de eventos de subproceso con un servidor en modo de detención (que Usa WinDbg) hará que gdbserver se bloquee. Si este valor es false (el valor predeterminado), se sintetizarán los eventos de inicio y detención del subproceso, pero pueden aparecer significativamente más tarde que en el tiempo real de creación o destrucción del subproceso. Cuando hay una corrección para ello disponible en gdbserver, los eventos reales se pueden habilitar a través de esta opción.

Conexión a un proceso en modo de usuario único

En esta sección se describe cómo identificar y conectarse a un proceso de modo de usuario único en Linux mediante WinDbg.

WSL (Subsistema de Windows para Linux)

En los ejemplos se usa WSL (subsistema Windows para Linux), pero también se pueden usar otras implementaciones de Linux. Para obtener información sobre la configuración y uso de WSL, consulte:

Selección del proyecto deseado

Enumere los procesos de Linux mediante el comando ps -A para determinar el proceso en ejecución al que se va a conectar.

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

En este tutorial de ejemplo, se conectará a python3.

Búsqueda de la dirección IP del sistema de destino

Si se va a conectar a un destino remoto de Linux, use un comando como ip route showpara determinar la dirección IP externa.

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

En este tutorial, nos conectaremos a WSL que se ejecuta en el mismo equipo y usaremos la dirección IP de localhost.

Conexión de GDBServer al proceso seleccionado

En la consola de Linux de WSL, escriba gdbserver localhost:1234 python3 para iniciar gdbserver en el puerto 1234 y conectarlo al proceso python3.

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

Para algunos entornos de Linux, es posible que el comando deba ejecutarse como administrador, por ejemplo, mediante sudo - sudo gdbserver localhost:1234 python3. Tenga cuidado al habilitar el acceso de nivel raíz del administrador del depurador y úselo solo cuando sea necesario.

Creación de la conexión del servidor de procesos en WinDbg

Abra WinDbg y seleccione "Archivo / Conectar al depurador remoto" y escriba una cadena de protocolo para la conexión. En este ejemplo se usará: gdb:server=localhost,port=1234.

captura de la pantalla Inicio de depuración de WinDbg que muestra cadena de conexión.

Una vez que haga clic en el botón Aceptar, el depurador debe conectarse al servidor gdbserver y debe estar en la interrupción inicial del proceso.

Una vez que esté en el punto de interrupción inicial, puede pulsar "g" varias veces. Obtendrá mensajes de carga de módulos (y los eventos de estilo sxe "interrupción en la carga del módulo" deben funcionar correctamente).

Tenga en cuenta que puede tardar un poco en llegar a ese punto, ya que los símbolos de depuración se cargan en la memoria caché. Además de buscar símbolos y archivos binarios a través del servidor de símbolos o la ruta de búsqueda local, la integración de GDBServer tiene la capacidad de extraer estos archivos del sistema de archivos remoto si no se encuentran a través de symsrv o localmente. Normalmente, se trata de una operación mucho más lenta que obtener símbolos de symsrv o una ruta de búsqueda local, pero hace que la experiencia general sea mejor mediante la localización de símbolos adecuados.

Use el comando k stacks para enumerar la pila. Muestra los módulos python3, por lo que se confirma que estamos depurando python3 en Linux mediante 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

En este momento, debería poder hacer casi todo lo que se puede hacer mediante WinDbg conectado a un depurador remoto de Windows a través de un servidor de procesos remotos. Puede recorrer código, depuración en el nivel de origen, establecer puntos de interrupción, inspeccionar variables locales, etc.

Una vez que haya terminado de depurar, use CTRL+D para salir de la ventana gbdserver en WSL.

Conexión a un servidor de procesos

Además de conectarse a un único proceso a través de un GDBServer en modo de usuario, puede configurar uno como servidor de procesos y una lista y adjuntarlo a los procesos existentes en el sistema. Para ello, gdbserver se inicia con el argumento de línea de comandos "--multi" - gdbserver --multi localhost:1234

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

Para conectarse al servidor de procesos, seleccione "Archivo / Conectar al servidor de procesos" en WinDbg y escriba la misma cadena de protocolo que hizo con el ejemplo de gdbserver de proceso único anterior:

gdb:server=localhost,port=1234

Una vez que haga clic en el botón "Aceptar", debería conectarse al servidor gdbserver como servidor de procesos. Al igual que con dbgsrv, puede generar un nuevo proceso o puede enumerar los procesos existentes y asociar a uno.

En este ejemplo, use la opción "Asociar al proceso".

captura de la pantalla Inicio de depuración de WinDbg que muestra la asociación al proceso con 20 o más procesos enumerados.

Tenga en cuenta que verá muchas de las mismas cosas que están visibles para los procesos de Windows (incluidos el PID, el usuario y la línea de comandos). Algunas de las columnas del cuadro de diálogo "adjuntar al proceso" no son relevantes para Linux y no contendrán datos.

Finalizar la sesión

Use CTRL+D para salir de la ventana gbdserver en WSL y seleccione Detener depuración en WinDbg. Para finalizar la sesión, en algunos casos es posible que tenga que salir del depurador.

Nueva conexión al servidor de procesos

WinDbg reconoce un "servidor de procesos" frente a un "destino único" a través de si el gdbserver está asociado a un proceso o no. Si se asocia a algún proceso, déjelo inmovilizado, cierre el depurador e intente volver a conectarse al servidor de procesos, con toda probabilidad, no lo reconoceremos como un servidor de procesos. En esta situación, reinicie el gdbserver de destino y vuelva a conectar el depurador.

Funcionalidad de WinDbg de Linux

Aunque gran parte de la funcionalidad del depurador funcionará según lo previsto en los volcados de núcleo de depuración (por ejemplo: recorrer la pila, símbolos, información de tipo, variables locales, desensamblado, etc.), es importante tener en cuenta que la cadena de herramientas de depuración completa aún no reconoce ELF, DWARF y las diferencias resultantes de la semántica de Windows. Actualmente, algunos comandos del depurador pueden dar lugar a una salida inesperada. Por ejemplo, lm seguirá mostrando información incorrecta para un módulo ELF, ya que espera y analiza manualmente los encabezados PE.

Modo kernel de Linux mediante EXDI

El depurador de Windows admite la depuración de kernel mediante EXDI. Esto permite depurar una amplia variedad de sistemas operativos y hardware. Para obtener información general sobre cómo configurar y solucionar problemas de conexiones de EXDI, consulte Configuración del transporte del depurador de EXDI.

Para obtener información sobre cómo configurar la depuración en modo kernel de QEMU mediante EXDI, consulte Configuración de la depuración en modo kernel de QEMU mediante EXDI.

Símbolos y orígenes de Linux

En esta sección se describen el uso básico y la disponibilidad de símbolos de Linux. Para obtener información más detallada, consulte Símbolos y orígenes de Linux y Acceso extendido de código fuente.

Servidores de símbolos DebugInfoD

A partir de la versión 1.2104 de WinDbg, el comando de ruta de acceso de origen (.srcpath, .lsrcpath (Establecer ruta de acceso de origen)) admite la recuperación de archivos de los servidores DebugInfoD a través de la etiqueta DebugInfoD*.

La etiqueta DebugInfoD* puede apuntar a uno o varios servidores DebugInfoD con cada dirección URL de servidor con formato https://domain.com y separados por *. Los servidores se buscarán en el mismo orden que se muestra en la ruta de acceso de origen y los archivos se recuperarán de la primera dirección URL coincidente. Para obtener más información, consulte Acceso extendido de código fuente.

Por ejemplo, puede usar el comando .sympath (Establecer ruta de acceso de símbolo) para establecer una ruta de acceso DebugInfoD como esta.

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

Para obtener información general sobre cómo establecer la ruta de acceso de símbolos, consulte Uso de símbolos.

Para mostrar información sobre los símbolos que se cargan, use !sym noisy. Para más información, consulte !sym.

También se admite la descarga automática de orígenes de servidores DebugInfoD que admiten la devolución de ese tipo de artefacto. Puede, en esencia, hacer lo siguiente:

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

Para obtener más información sobre cómo trabajar con símbolos DWARF y las utilidades de símbolos de Linux, como !sourcemap y !diesym, consulte Símbolos y orígenes de Linux.

Tutorial de la aplicación de C++

  1. Use un editor de texto (como nano o vi) para crear el archivo de C++. Por ejemplo:

nano DisplayGreeting.cpp

  1. En el editor de texto, escriba el programa de C++. Este es un programa sencillo que muestra saludos, que debe depurarse:
#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. Guarde (CTRL-O) y salga (CTRL-X) del editor nano.

  2. Compile el archivo de C++ mediante g++. La opción -o se usa para especificar el nombre del archivo de salida y la opción -g genera un archivo de símbolos:

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. Si no hay errores en el código, el comando g++ creará un archivo ejecutable denominado DisplayGreeting en el directorio.

  2. Puede ejecutar el programa con el comando siguiente:

./DisplayGreeting

  1. Al pulsar la tecla de retorno se muestra el mensaje en la aplicación. Al examinar la salida, parece que el saludo se está truncando y se muestra "????" en su lugar.

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

Depuración de DisplayGreeting

  1. Una vez que el código esté listo para ejecutarse, podemos iniciar la aplicación mediante el gdbserver.

gdbserver localhost:1234 DisplayGreeting

  1. Abra WinDbg y seleccione "Archivo / Conectar al depurador remoto" y escriba una cadena de protocolo para la conexión. En este ejemplo se usará: gdb:server=localhost,port=1234.

  2. Una vez conectado, la salida debe indicar que está escuchando en el puerto 1234 y que se ha establecido la conexión de depuración remota.

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

Como se mencionó anteriormente, para algunos entornos de Linux, es posible que el comando deba ejecutarse como administrador, normalmente mediante sudo. Tenga cuidado al habilitar el acceso de nivel raíz del administrador del depurador y úselo solo cuando sea necesario.

Adición de las rutas de acceso de origen y símbolo a la sesión del depurador

Para establecer puntos de interrupción y ver el código fuente y las variables, establezca los símbolos y la ruta de acceso de origen. Para obtener información general sobre cómo establecer la ruta de acceso de símbolos, consulte Uso de símbolos.

Use .sympath para agregar la ruta de acceso de símbolos a la sesión del depurador. En este ejemplo, el código se ejecuta en esta ubicación en WSL Linux Ubuntu para un usuario denominado Bob.

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

En WSL, este directorio se asigna a la ubicación del sistema operativo Windows de: C:\Users\Bob\

Por lo tanto, se usan estos dos comandos.

.sympath C:\Users\Bob\

.srcpath C:\Users\Bob\

Para obtener más información sobre el sistema de archivos de WSL, consulte Permisos de archivo para WSL.

  1. Para beneficiarse de símbolos adicionales del sistema operativo Linux, agregue los símbolos DebugInfoD mediante la ubicación .sympath, como esta.

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

  1. También se admite la descarga automática de orígenes de servidores DebugInfoD que admiten la devolución de ese tipo de artefacto. Para aprovechar esto, agregue el servidor elfutils mediante .srcpath.

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

Establecer un punto de interrupción

Establezca un punto de interrupción en el elemento principal de la aplicación 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

Utilice el comando Ir o la opción de menú para reiniciar la ejecución del código.

Carga del código fuente

Use el comando .reload para volver a cargar los símbolos.

Use el comando lm para confirmar que estamos ejecutando la aplicación 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

Una vez que el comando desencadena el acceso al código de saludo para mostrar, se mostrará en WinDbg.

captura de pantalla del código de DisplayGreeting.cpp en WinDbg con punto de interrupción establecido en la línea 19, wprint

Use el comando 'k' para enumerar la pila.

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

Use el comando dx para ver la variable local de saludo. Observe que su tamaño es 50.

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

Examine el código y observe que el tamaño de 50 puede no ser suficiente para el mensaje de saludo.

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

Para confirmarlo, expanda la variable local del saludo y vea que el saludo está truncado.

Solución de problemas de conexión de gdbserver

Use la opción --debug para mostrar información adicional en la consola de gdbserver para recopilar más información sobre el estado de conexión. Por ejemplo, para iniciar un servidor de procesos, use este comando.

gdbserver --debug --multi localhost:1234

Consulte también

Símbolos y orígenes de Linux

Acceso extendido de código fuente

Volcados de memoria de Linux

ELFUTILS debuginfod

Elección del mejor método de depuración remota