Compartilhar via


Depuração de processo remota ao vivo do Linux

Este artigo descreve como estabelecer uma conexão WinDbg ao vivo com o Linux. A depuração de processo remota ao vivo no Linux requer o WinDbg versão 1.2402.24001.0 ou superior.

O Depurador GNU - GDBServer é usado no Linux para dar suporte à conexão WinDbg. Para obter mais informações sobre GDBServer, consulte https://en.wikipedia.org/wiki/Gdbserver. Um lugar para ver a documentação para depuração remota de gdb é aqui - https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

Os exemplos aqui usam o WSL (Subsistema do Windows para Linux), mas outras implementações do Linux também podem ser usadas.

Tipos WinDbg de depuração de processo remota

Existem dois métodos principais de executar a depuração remota com o WinDbg - um servidor de processo ou um servidor de conexão KD. Os servidores de processo são usados para depuração no modo de usuário. Os servidores de conexão KD são usados para depuração no modo kernel. Confira Servidores de Processo (Modo de Usuário) e Servidores de Conexão KD (Modo Kernel) para obter informações gerais sobre esses tipos de conexão WinDbg.

Há duas maneiras de começar a depurar processos de modo de usuário do Linux. Você pode iniciar o gdbserver em um processo específico ou pode iniciar o gdbserver como um servidor de processo que pode listar e anexar a processos existentes. Isso é muito parecido com o servidor de processo DbgSrv (dbgsrv.exe) no Windows. Para obter mais informações, confira Ativar um servidor de processo.

Depuração de processo do Linux no modo de usuário

É possível conectar a um processo específico de modo de usuário único, ou em modo múltiplo, para ver todo o processo em uma lista e selecionar um para se conectar. Os dois métodos são descritos neste tópico. Os dois métodos compartilham a mesma sintaxe de cadeia de conexão, descrita a seguir.

Formato para a cadeia de conexão gdbserver

O formato usado para conectar ao gdbserver é "protocol:arguments", em que arguments é uma lista separada por vírgulas de "argument=value". Para uma conexão gdbserver no modo de usuário, o protocolo é gdb e o conjunto de argumentos é conforme descrito a seguir.

server=<address> - Obrigatório: indica o endereço IP do gdbserver ao qual se conectar.

port=<port> - Obrigatório: indica o número de porta do gdbserver ao qual se conectar.

threadEvents=<true|false> - Opcional: indica se os eventos de thread para esta versão do gdbserver funcionam corretamente no modo de parada.

Há um problema nas versões atuais do gdbserver, em que habilitar eventos de thread com um servidor no modo de parada (que o WinDbg usa) fará com que o gdbserver falhe. Se esse valor for false (o valor padrão), os eventos de início e parada do thread serão sintetizados, mas poderão aparecer significativamente mais tarde do que o tempo real de criação/destruição do thread. Quando uma correção para isso está disponível no gdbserver, os eventos reais podem ser habilitados por meio dessa opção.

Conectar a um processo de modo de usuário único

Esta seção descreve como identificar e conectar a um processo de modo de usuário único no Linux, usando o WinDbg.

WSL (Subsistema do Windows para Linux)

Os exemplos aqui usam o WSL (Subsistema do Windows para Linux), mas outras implementações do Linux podem ser usadas. Para obter mais informações sobre como configurar e usar o WSL, confira:

Selecionar o processo desejado

Liste os processos no Linux usando o comando ps -A para determinar o processo em execução ao qual se 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

Neste exemplo passo a passo, vamos nos conectar ao python3.

Localizar o endereço IP do sistema de destino

Se estiver se conectando a um destino remoto do Linux, use um comando como ip route show para determinar o endereço IP externo.

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

Neste passo a passo, nós nos conectaremos ao WSL em execução no mesmo PC e usaremos o endereço IP de localhost.

Anexar o GDBServer ao processo selecionado

No console do Linux do WSL, insira gdbserver localhost:1234 python3 para iniciar o gdbserver na porta 1234 e anexe-o ao processo python3.

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

Para alguns ambientes Linux, talvez seja necessário executar o comando como administrador, por exemplo, usando sudo - sudo gdbserver localhost:1234 python3. Tenha cuidado ao habilitar o acesso ao nível raiz do administrador do depurador e use isso somente quando for necessário.

Criar a conexão do servidor de processo no WinDbg

Abra o WinDbg e selecione "Arquivo / Conectar ao Depurador Remoto" e insira uma cadeia de caracteres de protocolo para a conexão. Neste exemplo, usaremos: gdb:server=localhost,port=1234.

captura de tela da tela Iniciar depuração do WinDbg mostrando a cadeia de conexão.

Depois de clicar no botão OK, o depurador deve se conectar ao gdbserver e você deve estar no processo inicial de início do intervalo.

Uma vez que você está no ponto de interrupção inicial, pode pressionar 'g' várias vezes. Você receberá mensagens de carregamento do módulo (e os eventos "interrupção no carregamento do módulo" no estilo sxe devem funcionar corretamente).

Pode demorar um pouco para chegar a esse ponto, pois os símbolos de depuração são carregados no cache. Além de procurar símbolos e binários por meio do servidor de símbolos ou do seu caminho de pesquisa local, a integração GDBServer tem a capacidade de extrair esses arquivos do sistema de arquivos remoto se eles não puderem ser encontrados por meio de symsrv ou localmente. Geralmente, essa é uma operação muito mais lenta do que obter símbolos do symsrv ou de um caminho de pesquisa local, mas torna a experiência geral melhor localizando os símbolos apropriados.

Use o comando k stacks para listar a pilha. Ele mostra módulos python3, e isso confirma que estamos depurando python3 no 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

Neste ponto, você deve conseguir fazer quase tudo o que pode ser feito usando o WinDbg conectado a um depurador remoto do Windows em um servidor de processo remoto. Você pode percorrer, depurar no nível do código-fonte, definir pontos de interrupção, inspecionar locais, etc.

Quando terminar a depuração, use CTRL+D para sair da janela do gbdserver no WSL.

Conectar a um servidor de processo

Além de se conectar a um único processo por meio de um GDBServer no modo de usuário, você pode configurar um como um servidor de processo e listar e anexar a processos existentes no sistema. Para fazer isso, o gdbserver é iniciado com o argumento de linha de comando "--multi" - gdbserver --multi localhost:1234.

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

Para se conectar ao servidor de processo, selecione "Arquivo / Conectar ao servidor de processo" no WinDbg e insira a mesma cadeia de caracteres de protocolo que você fez com o exemplo gdbserver de processo único acima:

gdb:server=localhost,port=1234

Depois de clicar no botão "OK", você deve estar conectado ao gdbserver como um servidor de processo. Como ocorre com o dbgsrv, você pode gerar um novo processo ou listar os processos existentes e anexar a um.

Neste exemplo, use a opção "Anexar ao processo".

captura de tela da tela Iniciar depuração do WinDbg mostrando anexar ao processo com 20 ou mais processos listados.

Você verá muitas das mesmas coisas que são visíveis para processos do Windows (incluindo o PID, o usuário e a linha de comando). Algumas das colunas na caixa de diálogo "anexar ao processo" não são relevantes para o Linux e não conterão dados.

Como encerrar a sessão

Use CTRL+D para sair da janela gbdserver no WSL e selecione parar depuração no WinDbg. Para encerrar a sessão, em alguns casos, pode ser necessário sair do depurador.

Reconectar ao servidor de processo

O WinDbg reconhece um "servidor de processo" em comparação com um "único destino" por meio de se o gdbserver está conectado a um processo ou não. Se você anexar a algum processo, deixá-lo congelado, fechar o depurador e tentar reconectar ao servidor de processo, muito provavelmente não o reconheceremos como um servidor de processo. Nessa situação, reinicie o gdbserver de destino e reconecte o depurador.

Funcionalidade WinDbg do Linux

Embora grande parte da funcionalidade do depurador funcione conforme o esperado na depuração de despejos principais (por exemplo: verificação de pilha, símbolos, informações de tipo, variáveis locais, desmontagem, etc...), é importante observar que toda a cadeia de ferramentas de depuração ainda não foi informada sobre ELF, DWARF e as diferenças resultantes da semântica do Windows. Alguns comandos no depurador podem resultar atualmente em saída inesperada. Por exemplo, lm ainda exibirá informações incorretas para um módulo ELF como ele espera e analisa manualmente os cabeçalhos PE.

Modo Kernel do Linux via EXDI

O depurador do Windows tem suporte para depuração de kernel usando EXDI. Isso permite depurar uma ampla variedade de hardware e sistemas operacionais. Para obter informações gerais sobre como definir a configuração e solucionar problemas de conexões EXDI, confira Configurar o transporte do depurador EXDI.

Para obter informações sobre como configurar a depuração do modo kernel QEMU usando EXDI, consulte Configurando a depuração do modo kernel QEMU usando EXDI.

Símbolos e fontes do Linux

Esta seção descreve o uso básico e a disponibilidade de símbolos do Linux. Para obter informações mais detalhadas, confira Símbolos e fontes do Linux e Acesso estendido ao código-fonte.

Servidores de símbolos DebugInfoD

Iniciando o WinDbg versão 1.2104, o comando source path (.srcpath, .lsrcpath (Definir Caminho de Origem)) oferece suporte à recuperação de arquivos de servidores DebugInfoD por meio da marcação DebugInfoD*.

A DebugInfoD* tag pode apontar para um ou mais servidores DebugInfoD com cada URL de servidor formatada como https://domain.com e separada por *. Os servidores serão pesquisados na mesma ordem listada no caminho de origem e os arquivos serão recuperados da primeira URL correspondente. Para obter mais informações, consulte Acesso estendido ao código-fonte.

Por exemplo, você pode usar o comando .sympath (Definir Caminho do Símbolo) para definir um caminho DebugInfoD como esse.

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

Para obter informações gerais sobre como definir o caminho dos símbolos, confira Usar símbolos.

Para exibir informações sobre símbolos que estão sendo carregados, use !sym noisy. Para obter mais informações, consulte !sym.

O download automático de fontes de servidores DebugInfoD que suportam o retorno desse tipo de artefato também tem suporte. Basicamente, você pode fazer:

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

Para obter mais informações sobre como trabalhar com símbolos DWARF e os utilitários de símbolos do Linux, como !sourcemap e !diesym, confira Símbolos e fontes do Linux.

Passo a passo do aplicativo C++

  1. Use um editor de texto (como nano ou vi) para criar o arquivo C++. Por exemplo:

nano DisplayGreeting.cpp

  1. No editor de texto, escreva o programa C++. Aqui está um programa simples que exibe saudações, que precisa ser depurado:
#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. Salve (CTRL-O) e saia (CTRL-X) do editor nano.

  2. Compile o arquivo C++ usando g++. A opção -o é usada para especificar o nome do arquivo de saída, e a opção -g gera um arquivo de símbolos:

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. Se não houver erros no código, o comando g++ criará um arquivo executável chamado DisplayGreeting no diretório.

  2. Você pode executar o programa usando o seguinte comando:

./DisplayGreeting

  1. Pressionar a tecla de retorno exibe a mensagem no aplicativo. Analisando a saída, parece que a Saudação está sendo truncada, e "????" está sendo exibido no lugar.

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

Depurar DisplayGreeting

  1. Quando o código estiver pronto para ser executado, poderemos iniciar o aplicativo usando o gdbserver.

gdbserver localhost:1234 DisplayGreeting

  1. Abra o WinDbg e selecione "Arquivo / Conectar ao Depurador Remoto" e insira uma cadeia de caracteres de protocolo para a conexão. Neste exemplo, usaremos: gdb:server=localhost,port=1234.

  2. Uma vez conectado, a saída deve indicar que está escutando na porta 1234 e que a conexão de depuração remota está estabelecida.

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 mencionado anteriormente, para alguns ambientes Linux, pode ser necessário executar o comando como um administrador, geralmente usando sudo. Tenha cuidado ao habilitar o acesso ao nível raiz do administrador do depurador e use isso somente quando for necessário.

Adicionar os caminhos de origem e símbolo à sessão do depurador

Para definir pontos de interrupção e exibir o código-fonte e as variáveis, defina os símbolos e o caminho de origem. Para obter informações gerais sobre como definir o caminho dos símbolos, confira Usar símbolos.

Use .sympath para adicionar o caminho do símbolo à sessão do depurador. Neste exemplo, o código está sendo executado neste local no WSL Linux Ubuntu, para um usuário chamado Bob.

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

No WSL, esse diretório é mapeado para o local do sistema operacional Windows de: C:\Users\Bob\.

Portanto, estes dois comandos são usados.

.sympath C:\Users\Bob\

.srcpath C:\Users\Bob\

Para obter mais informações sobre o sistema de arquivos WSL, confira Permissões de arquivo para WSL.

  1. Para se beneficiar de símbolos adicionais do sistema operacional Linux, adicione os símbolos DebugInfoD usando o local .sympath, como este.

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

  1. O download automático de fontes de servidores DebugInfoD que suportam o retorno desse tipo de artefato também tem suporte. Para tirar proveito disso, adicione o servidor elfutils usando .srcpath.

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

Definir um ponto de interrupção

Defina um ponto de interrupção na parte principal do aplicativo 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

Use o comando Go ou a opção de menu para reiniciar a execução do código.

Carregar o código-fonte

Use o comando .reload para recarregar os símbolos.

Use o comando lm para confirmar que estamos executando o aplicativo 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

Uma vez que o comando aciona o acesso ao código de exibição de saudação, ele será exibido no WinDbg.

captura de tela do código DisplayGreeting.cpp no WinDbg com ponto de interrupção definido na linha 19, wprint

Use o comando "k" para listar a pilha.

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 o comando dx para exibir a variável local greeting. Observe que seu tamanho é 50.

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

Examine o código e observe que 50 pode não ser um tamanho suficiente para a mensagem de saudação.

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

Confirme isso expandindo a variável local para a saudação e vendo que a saudação está truncada.

Solucionar problemas de conexão do gdbserver

Use a opção --debug para exibir informações adicionais no console do gdbserver para coletar mais informações sobre o status da conexão. Por exemplo, para iniciar um servidor de processo, use este comando.

gdbserver --debug --multi localhost:1234

Confira também

Símbolos e fontes do Linux

Acesso estendido ao código-fonte

Despejos de memória do Linux

ELFUTILS debuginfod

Escolher o melhor método de depuração remota