Содержание
Отладка программ с использованием GDB-сервера ядра KasperskyOS
GDB-сервер ядра имеет следующие особенности:
- Распознает процессы и потоки исполнения, управляемые KasperskyOS.
- Поддерживает отладку на аппаратных платформах с SMP (в том числе при эмуляции на QEMU).
Подготовка к отладке на QEMU
Чтобы подготовить условия для отладки с использованием GDB-сервера ядра KasperskyOS на QEMU, нужно выполнить следующие шаги:
- Проверить, что в составе KasperskyOS Community Edition есть версия ядра с GDB-сервером.
Для это нужно убедиться в наличии файла
libexec/aarch64-kos/kos-qemu-gdbstub
в составе KasperskyOS Community Edition. - Добавить параметр
GDBSTUB_KERNEL
в список параметровCMake
-командыbuild_kos_qemu_image()
.Этот параметр включает в решение ядро с GDB-сервером.
Пример:
build_kos_qemu_image(kos-qemu-image GDBSTUB_KERNEL ... QEMU_FLAGS "${QEMU_FLAGS}") - Создать файл
.gdbinit
, чтобы не вызывать вручную начальные GDB-команды при каждом запуске отладчика GDB. (Файл.gdbinit
нужно сохранить в директории, где будет запускаться отладчик GDB.)Пример:
# Задать путь для поиска динамических библиотек с отладочными символами из # состава KasperskyOS Community Edition set sysroot /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos # Задать дополнительные пути для поиска динамических библиотек с отладочными # символами set solib-search-path /home/user/example/build/lib1:/home/user/example/build/lib2 # Настроить управление потоками исполнения (опционально) # Отладчик GDB может работать в режиме all-stop или non-stop. В первом режиме # отладчик GDB считает, что при остановке потока исполнения, контекст которого # находится в фокусе отладки, останавливаются все остальные потоки исполнения, # о которых известно отладчику GDB. Во втором режиме отладчик GDB считает, что # все потоки, кроме остановленного, продолжают исполняться. Режим all-stop # применяется отладчиком GDB по умолчанию, и GDB-сервер ядра дает возможность # использовать только этот режим. Чтобы изменить заданные по умолчанию параметры # управления потоками исполнения в режиме all-stop, нужно использовать GDB-команды # set scheduler-locking и set schedule-multiple. # В этом примере первая GDB-команда указывает отладчику GDB, что при возобновлении # исполнения потока, контекст которого находится в фокусе отладки (например, # GDB-командами continue, step, next), нужно возобновить исполнение остальных потоков. # Вторая GDB-команда указывает отладчику GDB, что при возобновлении исполнения потока, # контекст которого находится в фокусе отладки, нужно возобновить исполнение всех # потоков, входящих в процессы всех программ, для которых выполняется отладка. set scheduler-locking off set schedule-multiple on # Подключить отладчик GDB к GDB-серверу ядра # GDB-сервер ядра взаимодействует с отладчиком GDB по расширенному протоколу, # и QEMU предоставляет доступ к GDB-серверу ядра через TCP-сокет. target extended-remote localhost:1234
Начальные шаги отладки на QEMU
Чтобы начать отладку с использованием GDB-сервера ядра KasperskyOS на QEMU, нужно выполнить следующие шаги:
- Собрать отладочные версии исполняемых файлов и библиотек, создать образ решения для QEMU и запустить QEMU.
Для этого нужно вызвать shell-команды
cmake
, указав параметры-D CMAKE_BUILD_TYPE:STRING=Debug
и--target sim
.Пример:
"Unix Makefiles" \ -D CMAKE_BUILD_TYPE:STRING=Debug \ -D CMAKE_TOOLCHAIN_FILE=$SDK_PREFIX/toolchain/share/toolchain-$TARGET.cmake \ -B build \ && cmake --build build --target sim... cmake -GВместо цели
sim
можно указать цельsim/fast
, чтобы не выполнять повторную сборку.QEMU запускается и исполняет код решения. Исполнение кода решения останавливается при подключении отладчика GDB.
- Запустить отладчик GDB и подключиться к GDB-серверу ядра.
Для этого нужно запустить исполняемый файл
toolchain/bin/aarch64-kos-gdb
из состава KasperskyOS Community Edition. Запуск нужно выполнить в директории, где сохранен созданный вручную файл.gdbinit
. - Создать инфериоры, привязать их к процессам и загрузить отладочные символы.
Чтобы загрузить отладочные символы, нужно переключиться на инфериор, ассоциированный с процессом, и использовать следующие GDB-команды:
add-symbol-file
<путь к файлу
> – для исполняемых файлов со статической или динамической компоновкой;file
<путь к файлу
> – для исполняемых файлов с динамической компоновкой.
GDB-команду
file
нужно использовать, чтобы отладчик GDB загрузил отладочные символы исполняемого файла и динамических библиотек, от которых зависит этот исполняемый файл. Чтобы отладчик GDB загрузил отладочные символы только исполняемого файла с динамической компоновкой, нужно использовать GDB-командуadd-symbol-file
.Если отладочные символы сохраняются не в исполняемых файлах, а в отдельных файлах, то в исполняемые файлы добавляются ссылки на файлы с отладочными символами. При вызове GDB-команды
add-symbol-file
илиfile
можно указать как исполняемый файл, так и файл с отладочными символами.
При повторной сборке (шаг 1) нужно завершить сессию отладки (выйти из отладчика GDB) и выполнить повторно шаги 2 и 3.
Подготовка к отладке на аппаратной платформе
Чтобы подготовить условия для отладки с использованием GDB-сервера ядра KasperskyOS на аппаратной платформе, нужно выполнить следующие шаги:
- Проверить, что в составе KasperskyOS Community Edition есть версии ядра с GDB-сервером.
Для это нужно убедиться в наличии файла
libexec/aarch64-kos/kos-gdbstub
в составе KasperskyOS Community Edition. - Скоммутировать компьютер, на котором будет работать отладчик GDB, с аппаратной платформой.
Для подключения компьютера к аппаратной платформе нужно использовать преобразователи USB-UART. Схемы соединения преобразователей USB-UART и аппаратных платформ см. в "Подготовка Raspberry Pi 4 B к запуску примеров" и "Подготовка Radxa ROCK 3A к запуску примеров".
- Добавить параметр
GDBSTUB_KERNEL
в список параметровCMake
-командыbuild_kos_hw_image()
.Этот параметр включает в решение ядро с GDB-сервером.
Пример:
build_kos_hw_image(kos-image GDBSTUB_KERNEL ... IMAGE_FILES ${ENTITIES}) - Создать файл
.gdbinit
, чтобы не вызывать вручную начальные GDB-команды при каждом запуске отладчика GDB. (Файл.gdbinit
нужно сохранить в директории, где будет запускаться отладчик GDB.)Пример:
# Задать путь для поиска динамических библиотек с отладочными символами из # состава KasperskyOS Community Edition set sysroot /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos # Задать дополнительные пути для поиска динамических библиотек с отладочными # символами set solib-search-path /home/user/example/build/lib1:/home/user/example/build/lib2 # Настроить управление потоками исполнения (опционально) # Отладчик GDB может работать в режиме all-stop или non-stop. В первом режиме # отладчик GDB считает, что при остановке потока исполнения, контекст которого # находится в фокусе отладки, останавливаются все остальные потоки исполнения, # о которых известно отладчику GDB. Во втором режиме отладчик GDB считает, что # все потоки, кроме остановленного, продолжают исполняться. Режим all-stop # применяется отладчиком GDB по умолчанию, и GDB-сервер ядра дает возможность # использовать только этот режим. Чтобы изменить заданные по умолчанию параметры # управления потоками исполнения в режиме all-stop, нужно использовать GDB-команды # set scheduler-locking и set schedule-multiple. # В этом примере первая GDB-команда указывает отладчику GDB, что при возобновлении # исполнения потока, контекст которого находится в фокусе отладки (например, # GDB-командами continue, step, next), нужно возобновить исполнение остальных потоков. # Вторая GDB-команда указывает отладчику GDB, что при возобновлении исполнения потока, # контекст которого находится в фокусе отладки, нужно возобновить исполнение всех # потоков, входящих в процессы всех программ, для которых выполняется отладка. set scheduler-locking off set schedule-multiple on # Задать символьную скорость для взаимодействия с GDB-сервером ядра # Нужно задать значение 115200 бод. set serial baud 115200 # Подключить отладчик GDB к GDB-серверу ядра # Нужно задать порт USB, через который подключен преобразователь USB-UART, # используемый для отладки. Также необходимо задать, что GDB-сервер ядра # взаимодействует с отладчиком GDB по расширенному протоколу. target extended-remote /dev/ttyUSB1
Начальные шаги отладки на аппаратной платформе
Чтобы начать отладку с использованием GDB-сервера ядра KasperskyOS на аппаратной платформе, нужно выполнить следующие шаги:
- Собрать отладочные версии исполняемых файлов и библиотек и создать образ решения для аппаратной платформы.
Для этого нужно вызвать shell-команды
cmake
, указав параметры-D CMAKE_BUILD_TYPE:STRING=Debug
и--target
<tgt
>, гдеtgt
– значение параметраNAME
вCMake
-командеbuild_kos_hw_image()
.Пример:
"Unix Makefiles" \ -D CMAKE_BUILD_TYPE:STRING=Debug \ -D CMAKE_TOOLCHAIN_FILE=$SDK_PREFIX/toolchain/share/toolchain-$TARGET.cmake \ -B build \ && cmake --build build --target kos-image... cmake -G - Загрузить образ решения на аппаратную платформу и запустить решение.
Исполнение кода решения останавливается при подключении отладчика GDB.
- Запустить отладчик GDB и подключиться к GDB-серверу ядра.
Для этого нужно запустить исполняемый файл
toolchain/bin/aarch64-kos-gdb
из состава KasperskyOS Community Edition. Запуск нужно выполнить в директории, где сохранен созданный вручную файл.gdbinit
. - Создать инфериоры, привязать их к процессами и загрузить отладочные символы.
Чтобы загрузить отладочные символы, нужно переключиться на инфериор, ассоциированный с процессом, и использовать следующие GDB-команды:
add-symbol-file
<путь к файлу
> – для исполняемых файлов со статической или динамической компоновкой;file
<путь к файлу
> – для исполняемых файлов с динамической компоновкой.
GDB-команду
file
нужно использовать, чтобы отладчик GDB загрузил отладочные символы исполняемого файла и динамических библиотек, от которых зависит этот исполняемый файл. Чтобы отладчик GDB загрузил отладочные символы только исполняемого файла с динамической компоновкой, нужно использовать GDB-командуadd-symbol-file
.Если отладочные символы сохраняются не в исполняемых файлах, а в отдельных файлах, то в исполняемые файлы добавляются ссылки на файлы с отладочными символами. При вызове GDB-команды
add-symbol-file
илиfile
можно указывать как исполняемый файл, так и файл с отладочными символами.
При повторной сборке (шаг 1) нужно завершить сессию отладки (выйти из отладчика GDB) и выполнить повторно шаги 2-4.
Работа с процессами и потоками исполнения
В отладчике GDB для работы с процессами используются объекты, которые называются инфериорами (англ. inferiors). Процесс каждой программы, которую нужно отладить, должен быть привязан к отдельному инфериору. Переключение между инфериорами соответствует переключению фокуса отладки в контекст другого процесса. Отладочные символы для программы, которую требуется отладить, нужно загружать после переключения на инфериор, который привязан к процессу этой программы.
При запуске отладчика GDB автоматически создается инфериор. При подключении отладчика GDB к GDB-серверу ядра исполнение кода решения останавливается, и этот инфериор автоматически привязывается к процессу, содержащему поток исполнения, в контексте которого останавливается исполнение кода решения. Остальные требуемые инфериоры могут быть созданы, привязаны к процессам и отвязаны от процессов вручную или автоматически. В первом случае нужно использовать GDB-команды для работы с инфериорами. Во втором случае требуется настроить обработку событий жизненного цикла процессов.
GDB-команды для работы с инфериорами
Для работы с инфериорами нужно использовать следующие GDB-команды:
info inferiors
Выводит список инфериоров. Текущий инфериор отмечается символом
*
. (Контекст процесса, ассоциированного с текущим инфериором, находится в фокусе отладки.)add-inferior
Создает инфериор.
inferior
<номер инфериора
>Переключает на другой инфериор.
attach
<PID
>Привязывает текущий инфериор к процессу с идентификатором PID. Чтобы вывести список процессов, нужно выполнить GDB-команду
info os processes
.detach
Отвязывает текущий инфериор от процесса.
Настройка обработки событий жизненного цикла процессов
GDB-сервер ядра сообщает отладчику GDB о наступлении следующих событий:
- создание дочернего процесса;
- запуск начального потока исполнения дочернего процесса;
- завершение процесса.
Отладчик GDB обрабатывает событие 1 только в том случае, если существует инфериор, привязанный к родительскому процессу. Для обработки события 2 необходимо, чтобы отладчик GDB предварительно обработал событие 1 при создании дочернего процесса. При этом событие 2 обрабатывается, даже если родительский процесс уже отвязан от инфериора, например, по причине завершения.
Чтобы настроить обработку событий 1 и 2, нужно использовать следующие GDB-команды:
set follow-fork-mode
{child
|parent
}Указывает отладчику GDB перейти в контекст дочернего процесса (
child
) или остаться в контексте родительского процесса (parent
) при наступлении события 1. Второй вариант выполняется по умолчанию.set detach-on-fork
{on
|off
}Указывает отладчику GDB оставить родительский или дочерний процесс привязанным к инфериору (
on
) или оставить родительский процесс привязанным к инфериору, а также создать новый инфериор и привязать его к дочернему процессу (off
) при наступлении события 1. Первый вариант выполняется по умолчанию. При этом, какой процесс привязан к инфериору (родительский или дочерний), зависит от параметра GDB-командыset follow-fork-mode
.set follow-exec-mode
{new
|same
}Указывает отладчику GDB создать новый инфериор и привязать его к дочернему процессу (
new
) или не делать этого (same
) при наступлении события 2. Второй вариант выполняется по умолчанию.
Рекомендуется настроить обработку событий 1 и 2 следующим образом:
Такая конфигурация обеспечивает обработку событий 1 и 2 отладчиком GDB следующим образом:
- При наступлении события 1 создается новый инфериор, который привязывается к дочернему процессу.
- При наступлении события 1 родительский процесс остается привязанным к инфериору.
- При наступлении события 2 не создается новый инфериор для дочернего процесса.
Обработку отладчиком GDB события 2 можно использовать для остановки исполнения программы до передачи управления функции main()
, чтобы не применять бесконечный цикл.
Чтобы исполнение программы остановилось при наступлении события 2, нужно использовать следующую GDB-команду:
Эта GDB-команда обеспечивает остановку исполнения каждого дочернего процесса, при создании которого было обработано событие 1. Чтобы выполнить остановку исполнения отдельных процессов, нужно использовать следующую GDB-команду:
Последующий вызов этой GDB-команды отменяет предыдущий, а ее вызов без параметров означает остановку каждого дочернего процесса, при создании которого было обработано событие 1.
Событие 3 обрабатывается отладчиком GDB только в том случае, если созданы инфериоры, привязанные к завершившимся процессам. При обработке этого события отладчик GDB отвязывает инфериор от завершившегося процесса. Обработку события 3 не требуется настраивать.
GDB-команды для работы с потоками исполнения
Чтобы вывести список потоков исполнения, которые содержатся в процессах, привязанных к инфериорам, нужно выполнить следующую GDB-команду:
Текущий поток исполнения отмечается символом *
. (Контекст текущего потока исполнения находится в фокусе отладки.)
Чтобы перейти в контекст другого потока исполнения, нужно выполнить следующую GDB-команду:
Переход в контекст другого потока исполнения приводит к переключению на другой инфериор, если этот поток исполнения содержится процессе, привязанном к инфериору, который не является текущим.
В начало