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:
- Como instalar o Linux no Windows com o WSL
- Configurar um ambiente de desenvolvimento do WSL
- Acessar aplicativos de rede com o WSL
- Configurações do Intune para WSL
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
.
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".
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++
- Use um editor de texto (como nano ou vi) para criar o arquivo C++. Por exemplo:
nano DisplayGreeting.cpp
- 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;
}
Salve (CTRL-O) e saia (CTRL-X) do editor nano.
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
Se não houver erros no código, o comando g++ criará um arquivo executável chamado DisplayGreeting no diretório.
Você pode executar o programa usando o seguinte comando:
./DisplayGreeting
- 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
- Quando o código estiver pronto para ser executado, poderemos iniciar o aplicativo usando o gdbserver.
gdbserver localhost:1234 DisplayGreeting
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
.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.
- 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
- 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.
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