Отладка удаленного процесса Linux
В этой статье описывается, как установить динамическое подключение WinDbg к Linux. Для отладки удаленного процесса в Linux требуется WinDbg версии 1.2402.24001.0 или более поздней.
Отладчик GNU — GDBServer используется в Linux для поддержки подключения WinDbg. Дополнительные сведения о GDBServer см. в статье https://en.wikipedia.org/wiki/Gdbserver. Здесь можно просмотреть документацию по удаленной отладке gdb. https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging
В примерах здесь используется подсистема Windows для Linux (WSL), но также можно использовать другие реализации Linux.
Типы WinDbg удаленной отладки процессов
Существует два основных метода выполнения удаленной отладки с помощью WinDbg — сервера обработки или сервера подключения KD. Серверы обработки используются для отладки в пользовательском режиме; Серверы подключения KD используются для отладки в режиме ядра. Общие сведения об этих типах подключений WinDbg см. в разделе "Серверы обработки" (режим пользователя) и серверы подключений KD (режим ядра).
Существует два способа запуска отладки процессов пользовательского режима Linux. Вы можете запустить gdbserver в определенном процессе или запустить gdbserver как сервер обработки, который может перечислять и присоединять к существующим процессам. Это так же, как сервер обработки DbgSrv (dbgsrv.exe) в Windows. Дополнительные сведения см. в разделе "Активация сервера обработки".
Отладка процесса Linux в пользовательском режиме
Можно подключиться к определенному процессу пользовательского режима или в нескольких режимах, чтобы просмотреть весь процесс в списке и выбрать один для подключения. Оба метода описаны в этом разделе. Оба метода используют один и тот же синтаксис строка подключения, описанный далее.
Формат строка подключения gdbserver
Формат, используемый для подключения к gdbserver, — "protocol:arguments", где аргументы — это разделенный запятыми список "argument=value". Для подключения gdbserver в пользовательском режиме протокол — gdb, и набор аргументов выглядит следующим образом.
server=<address>
— обязательный: указывает IP-адрес gdbserver для подключения.
port=<port>
— Обязательный: указывает номер порта gdbserver для подключения.
threadEvents=<true|false>
— Необязательный: указывает, работают ли события потока для этой версии gdbserver правильно в режиме остановки.
В текущих выпусках gdbserver возникает проблема, при которой включение событий потока с сервером в режиме остановки (которое использует WinDbg) приведет к сбою gdbserver. Если это значение равно false (значение по умолчанию), события запуска и остановки потока будут синтезированы, но могут отображаться значительно позже фактического времени создания или уничтожения потока. Если исправление для этого доступно в gdbserver, фактические события можно включить с помощью этого параметра.
Подключение к одному процессу пользовательского режима
В этом разделе описывается, как определить и подключиться к одному процессу пользовательского режима в Linux с помощью WinDbg.
WSL (подсистема Windows для Linux)
В примерах здесь используется WSL (подсистема Windows для Linux), но можно использовать другие реализации Linux. Сведения о настройке и использовании WSL см. в следующей статье:
- Установка Linux в Windows с помощью WSL
- Настройка среды разработки WSL
- Доступ к сетевым приложениям с помощью WSL
- Параметры Intune для WSL
Выберите нужный процесс
Вывод списка процессов в Linux с помощью ps -A
команды для определения выполняемого процесса для подключения.
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
В этом примере пошагового руководства мы подключимся к Python3.
Поиск IP-адреса целевой системы
При подключении к удаленному целевому объекту Linux используйте команду, например ip route show
, чтобы определить внешний IP-адрес.
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
В этом пошаговом руководстве мы подключимся к WSL, работающему на одном компьютере, и будем использовать IP-адрес localhost.
Присоединение GDBServer к выбранному процессу
В консоли Linux WSL введите gdbserver localhost:1234 python3
файл gdbserver через порт 1234 и подключите его к процессу Python3.
USER1@USER1:/mnt/c/Users/USER1$ gdbserver localhost:1234 python3
Process python3 created; pid = 1211
Listening on port 1234
Для некоторых сред Linux может потребоваться выполнить команду от имени администратора, например с помощью sudo - sudo gdbserver localhost:1234 python3
. Используйте осторожность, чтобы включить доступ к корневому уровню администратора отладчика и использовать его только в том случае, если это необходимо.
Создание подключения сервера обработки в WinDbg
Откройте WinDbg и выберите "Файл / подключение к удаленному отладчику" и введите строку протокола для подключения. В этом примере мы будем использовать: gdb:server=localhost,port=1234
После нажатия кнопки "ОК" отладчик должен подключиться к gdbserver и начать начальный процесс.
Когда вы находитесь в начальной точке останова, вы можете нажать клавишу G несколько раз. Вы получите сообщения о загрузке модуля (и стиль sxe "разрыв при загрузке модуля" должен работать должным образом).
Обратите внимание, что для этого момента может потребоваться некоторое время, так как символы отладки загружаются в кэш. Помимо поиска символов и двоичных файлов через сервер символов или локальный путь поиска, интеграция GDBServer имеет возможность извлечения этих файлов из удаленной файловой системы, если они не удается найти через symsrv или локально. Обычно это гораздо медленная операция, чем получение символов из symsrv или локального пути поиска, но это делает общий интерфейс более лучшим, установив соответствующие символы.
Используйте команду k stacks для перечисления стека. В нем показаны модули Python3, поэтому это подтверждает, что мы отладим python3 в Linux с помощью 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
На этом этапе вы сможете сделать почти все, что можно сделать с помощью WinDbg, подключенного к удаленному отладчику Windows на удаленном сервере обработки. Вы можете выполнить шаг, отладку на уровне источника, задать точки останова, проверить локальные и т. д.
После завершения отладки используйте CTRL+D, чтобы выйти из окна gbdserver в WSL.
Подключение к серверу обработки
Помимо подключения к одному процессу через GDBServer пользовательского режима, вы можете настроить его как сервер обработки и добавить к существующим процессам в системе. Для этого gdbserver запускается с аргументом командной строки --multi . gdbserver --multi localhost:1234
user1@USER1:/mnt/c/Users/USER1$ sudo gdbserver --multi localhost:1234
Listening on port 1234
Чтобы подключиться к серверу обработки, выберите "Файл и подключение к серверу обработки" в WinDbg и введите ту же строку протокола, что и в примере gdbserver одного процесса, приведенного выше:
gdb:server=localhost,port=1234
После нажатия кнопки "ОК" необходимо подключиться к gdbserver в качестве сервера обработки. Как и при использовании dbgsrv, можно либо создать новый процесс, либо перечислить существующие процессы и подключить к нему.
В этом примере используйте параметр "Присоединить к процессу".
Обратите внимание, что вы увидите множество одинаковых элементов, видимых для процессов Windows (включая PID, пользователя и командную строку). Некоторые столбцы в диалоговом окне "Присоединение к процессу" не относятся к Linux и не будут содержать данные.
Завершение сеанса
Нажмите клавиши CTRL+D, чтобы выйти из окна gbdserver в WSL и остановить отладку в WinDbg. Чтобы завершить сеанс, в некоторых случаях может потребоваться выйти из отладчика.
Повторное подключение к серверу обработки
WinDbg распознает "сервер обработки" и "один целевой объект" через присоединение gdbserver к процессу или нет. Если вы подключаетесь к некоторым процессам, оставьте его замороженным, закройте отладчик и попытаетесь повторно подключиться к серверу обработки, по всей вероятности, мы не будем распознавать его как сервер обработки. В этой ситуации перезапустите целевой gdbserver и повторно подключите отладчик.
Функции WinDbg для Linux
Хотя большая часть функциональных возможностей отладчика будет работать должным образом", при отладке основных дампов (например, стека, символов, сведений о типах, локальных переменных, дизассембли и т. д.), важно отметить, что вся цепочка инструментов отладки еще не была осведомлена о ELF, DWARF и результирующей разнице в семантике Windows. Некоторые команды в отладчике в настоящее время могут привести к непредвиденным выходным данным. Например, по-прежнему отображаются неверные сведения для модуля ELF, lm
так как он ожидает и вручную анализирует заголовки PE.
Режим ядра Linux через EXDI
Отладчик Windows поддерживает отладку ядра с помощью EXDI. Это позволяет выполнять отладку различных аппаратных и операционных систем. Общие сведения о настройке и устранении неполадок подключений EXDI см. в разделе "Настройка транспорта отладчика EXDI".
Сведения о настройке отладки режима ядра QEMU с помощью EXDI см. в разделе Настройка отладки режима ядра QEMU с помощью EXDI.
Символы и источники Linux
В этом разделе описаны основные возможности использования и доступности символов Linux. Дополнительные сведения см. в разделе "Символы и источники Linux" и "Расширенный доступ к исходному коду".
Серверы символов DebugInfoD
Начиная с WinDbg версии 1.2104 команда исходного пути (Srcpath, .lsrcpath (Set Source Path)) поддерживает извлечение файлов из серверов DebugInfoD с помощью тега DebugInfoD*
.
Тег DebugInfoD*
может указывать на один или несколько серверов DebugInfoD с каждым URL-адресом сервера, отформатированным как https://domain.com
и разделенным *
. Серверы будут искать в том же порядке, что и в исходном пути, и файлы будут получены из первого соответствующего URL-адреса. Дополнительные сведения см. в статье "Расширенный доступ к исходному коду".
Например, можно использовать команду Sympath (Задать путь к символам), чтобы задать путь DebugInfoD, как показано ниже.
.sympath+ DebugInfoD*https://debuginfod.elfutils.org
Общие сведения о настройке пути символов см. в разделе "Использование символов".
Чтобы отобразить сведения о загружаемых символах, используйте !sym noisy
. Дополнительные сведения см. в разделе !sym.
Также поддерживается автоматическая загрузка источников с серверов DebugInfoD, которые поддерживают возврат этого типа артефакта. Вы можете, по сути, сделать следующее:
.srcpath+ DebugInfoD*https://debuginfod.elfutils.org
Дополнительные сведения о работе с символами DWARF и служебными программами символов Linux, такими как !sourcemap
и !diesym
, см. в разделе "Символы и источники Linux".
Пошаговое руководство по приложению C++
- Для создания файла C++ используйте текстовый редактор (например, nano или vi). Например:
nano DisplayGreeting.cpp
- В текстовом редакторе напишите программу C++. Ниже приведена простая программа, которая отображает приветствия, которая должна быть отлаживаема:
#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;
}
Сохраните (CTRL-O) и закройте (CTRL-X) редактор nano.
Скомпилируйте файл C++ с помощью g++. Параметр -o используется для указания имени выходного файла, а параметр -g создает файл символов:
g++ DisplayGreeting.cpp -g -o DisplayGreeting
Если в коде нет ошибок, команда g++ создаст исполняемый файл с именем DisplayGreeting в каталоге.
Вы можете запустить программу с помощью следующей команды:
./DisplayGreeting
- При нажатии клавиши возврата отображается сообщение в приложении. Глядя на выходные данные, он выглядит так, как приветствие усечено, а вместо этого отображается "????".
HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YO????
Отладка DisplayGreeting
- Когда код будет готов к выполнению, мы можем запустить приложение с помощью gdbserver.
gdbserver localhost:1234 DisplayGreeting
Откройте WinDbg и выберите "Файл / подключение к удаленному отладчику" и введите строку протокола для подключения. В этом примере мы будем использовать:
gdb:server=localhost,port=1234
После подключения выходные данные должны указывать на то, что он прослушивается через порт 1234 и устанавливается удаленное подключение к отладке.
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
Как упоминалось ранее, для некоторых сред Linux может потребоваться выполнить команду от имени администратора, обычно используя sudo. Используйте осторожность, чтобы включить доступ к корневому уровню администратора отладчика и использовать его только в том случае, если это необходимо.
Добавление путей к источнику и символам в сеанс отладчика
Чтобы задать точки останова и просмотреть исходный код и переменные, задайте символы и исходный путь. Общие сведения о настройке пути символов см. в разделе "Использование символов".
Используйте .sympath
для добавления пути символа в сеанс отладчика. В этом примере код выполняется в этом расположении в Ubuntu WSL для пользователя с именем Bob.
\\wsl$\Ubuntu\mnt\c\Users\Bob\
В WSL этот каталог сопоставляется с расположением ОС Windows: C:\Users\Bob\
Поэтому используются эти две команды.
.sympath C:\Users\Bob\
.srcpath C:\Users\Bob\
Дополнительные сведения о файловой системе WSL см. в разделе "Разрешения файлов" для WSL.
- Чтобы воспользоваться дополнительными символами ОС Linux, добавьте символы DebugInfoD с помощью расположения Sympath, как показано ниже.
.sympath+ DebugInfoD*https://debuginfod.elfutils.org
- Также поддерживается автоматическая загрузка источников с серверов DebugInfoD, которые поддерживают возврат этого типа артефакта. Чтобы воспользоваться этим, добавьте сервер elfutils с помощью Srcpath.
.srcpath+ DebugInfoD*https://debuginfod.elfutils.org
Установка точки останова
Задайте точку останова в главной части приложения 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
Чтобы перезапустить выполнение кода, используйте команду Go или пункт меню.
Загрузка исходного кода
Используйте команду RELOAD, чтобы перезагрузить символы.
lm
Используйте команду, чтобы убедиться, что мы запускаем приложение 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
После активации команды доступа к отображаемого кода приветствия он будет отображаться в WinDbg.
Используйте команду 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```
Используйте команду dx для просмотра приветствия локальной переменной. Обратите внимание, что размер составляет 50.
0:000> dx greeting
greeting : { size=50 } [Type: std::array<wchar_t, 50>]
[<Raw View>] [Type: std::array<wchar_t, 50>]
Просмотрите код и обратите внимание, что 50, может быть недостаточного размера для приветствия.
wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL
Подтвердите это, разверив переменную locals для приветствия и увидев, что приветствие усечено.
Устранение неполадок подключения gdbserver
--debug
Используйте параметр для отображения дополнительных сведений в консоли gdbserver, чтобы получить дополнительные сведения о состоянии подключения. Например, чтобы запустить сервер обработки, используйте эту команду.
gdbserver --debug --multi localhost:1234