Отладка в Visual Studio Code
Настройка отладки в Visual Studio Code
· 8 min read
Добрался до момента, когда необходимо зафиксировать знания об отладке в Visual Studio Code. В этой статье приведу пример автоматизации сборки и отладки проекта на примере проекта open-isns - конфигурационные файлы сборки проекта в VS Code, а также запуск отладки с повышенными привелегиями. Думаю, некоторые из примеров можно переписать/адаптировать к своим проектам, используя их в качестве шаблонов.
Автоматизация сборки #
В качестве примера сборки отладки использую проект open-isns. Клонирую его с удаленного репозитория:
git clone https://github.com/open-iscsi/open-isns.git
Данный проект собирается с помощью систем сборки Meson и Ninja. Для данного проекта я создал следующую конфигурацию сборки, которая детально представлена ниже.
tasks.json #
Пользовательские задачи представлены в виде трёх частей:
- Meson Setup: Настраивает проект с помощью Meson, создавая директорию сборки и применяя указанные параметры.
- Ninja Build: Компилирует и собирает проект с помощью Ninja, завися от успешного выполнения
Meson Setup
. Это задача по умолчанию для сборки. - Clean Build: Удаляет директорию сборки для полной очистки.
Meson Setup #
{
"label": "Meson Setup",
"type": "shell",
"command": "meson",
"args": [
"setup", "${workspaceFolder}/builddir",
"-Ddebug=true",
"--default-library=shared",
"-Dsecurity=enabled"
],
"group": "build",
"problemMatcher": []
}
-
label
:"Meson Setup"
- Уникальное имя задачи, отображаемое в интерфейсе VS Code (например, в меню выбора задач).
-
type
:"shell"
- Указывает, что команда будет выполняться в оболочке (shell).
-
command
:"meson"
- Команда, которая будет выполнена.
-
args
- Массив аргументов, передаваемых команде
meson
:"setup"
: Подкоманда Meson, которая инициализирует проект и создает конфигурацию сборки."${workspaceFolder}/builddir"
: Путь к директории, где будут храниться файлы сборки. Переменная${workspaceFolder}
заменяется на путь к корневой папке проекта в VS Code. В данном случае директория сборки —builddir
внутри корневой папки."-Ddebug=true"
: Устанавливает опцию сборки Meson, включающую отладочную информацию (например, символы отладки для дебаггера)."--default-library=shared"
: Указывает, что по умолчанию проект будет собираться как разделяемая библиотека (shared library,.so
), а не статическая (.a
)."-Dsecurity=enabled"
: Пользовательская опция сборки проекта, которая включает функции безопасности.
- Массив аргументов, передаваемых команде
-
group
:"build"
- Указывает, что задача относится к группе задач сборки (
build
). Это позволяет VS Code классифицировать задачу и отображать её в контексте операций сборки (например, при вызове командыRun Build Task
).
- Указывает, что задача относится к группе задач сборки (
-
problemMatcher
:[]
- Список шаблонов для анализа вывода команды и выявления ошибок/предупреждений. Пустой массив
[]
означает, что для этой задачи не используется анализ проблем (ошибки компиляции или предупреждения не будут подсвечиваться в редакторе).
- Список шаблонов для анализа вывода команды и выявления ошибок/предупреждений. Пустой массив
Ninja Build #
{
"label": "Ninja Build",
"type": "shell",
"command": "ninja",
"args": [
"-C", "${workspaceFolder}/builddir"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$gcc"],
"dependsOn": ["Meson Setup"]
}
-
label
:"Ninja Build"
- Имя задачи, отображаемое в интерфейсе. Это основная задача для сборки проекта.
-
type
:"shell"
- Как и в предыдущей задаче, команда выполняется в оболочке.
-
command
:"ninja"
- Команда для выполнения —
ninja
, инструмент сборки, который используется для компиляции и сборки проекта на основе конфигурации, созданной Meson.
- Команда для выполнения —
-
args
- Аргументы для команды
ninja
:"-C"
: Указывает Ninja, в какой директории выполнять сборку."${workspaceFolder}/builddir"
: Путь к директории сборки, где находятся файлы, созданные задачейMeson Setup
.
- Аргументы для команды
-
group
- Объект, описывающий группу задачи:
kind
:"build"
: Указывает, что это задача сборки.isDefault
:true
: Делает задачуNinja Build
задачей по умолчанию для группыbuild
. Это означает, что она будет выполняться при вызове командыRun Build Task
(Ctrl+Shift+B в VS Code).
- Объект, описывающий группу задачи:
-
problemMatcher
:["$gcc"]
- Указывает, что для анализа вывода команды будет использоваться шаблон
$gcc
. Этот шаблон позволяет VS Code распознавать ошибки и предупреждения, выдаваемые компилятором GCC (или совместимыми, такими как Clang), и подсвечивать их в редакторе (например, в панели проблем или в коде).
- Указывает, что для анализа вывода команды будет использоваться шаблон
-
dependsOn
:["Meson Setup"]
- Указывает зависимость от задачи
Meson Setup
. Это означает, что перед выполнением задачиNinja Build
автоматически выполнится задачаMeson Setup
, чтобы гарантировать наличие актуальной конфигурации сборки.
- Указывает зависимость от задачи
Clean Build #
{
"label": "Clean Build",
"type": "shell",
"command": "rm -rf ${workspaceFolder}/builddir",
"group": "build",
"problemMatcher": []
}
-
label
:"Clean Build"
- Имя задачи, используемое для её идентификации в VS Code.
-
type
:"shell"
- Команда выполняется в оболочке.
-
command
:"rm -rf ${workspaceFolder}/builddir"
- Команда для выполнения: удаление директории
builddir
и всех её содержимого. Флаги:-r
: Рекурсивное удаление (включая поддиректории).-f
: Игнорирование ошибок, если директория не существует.${workspaceFolder}/builddir
: Путь к директории сборки.
- Команда для выполнения: удаление директории
-
group
:"build"
- Задача относится к группе задач сборки, но не является задачей по умолчанию.
-
problemMatcher
:[]
- Как и в задаче
Meson Setup
, анализ проблем не используется, так как командаrm
не генерирует вывод, связанный с ошибками компиляции.
- Как и в задаче
Конечная конфигурация #
{
"version": "2.0.0",
"tasks": [
{
"label": "Meson Setup",
"type": "shell",
"command": "meson",
"args": [
"setup", "${workspaceFolder}/builddir",
"-Ddebug=true",
"--default-library=shared",
"-Dsecurity=enabled"
],
"group": "build",
"problemMatcher": []
},
{
"label": "Ninja Build",
"type": "shell",
"command": "ninja",
"args": [
"-C", "${workspaceFolder}/builddir"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$gcc"],
"dependsOn": ["Meson Setup"]
},
{
"label": "Clean Build",
"type": "shell",
"command": "rm -rf ${workspaceFolder}/builddir",
"group": "build",
"problemMatcher": []
}
]
}
launch.json #
Файл launch.json
настроен для отладки компилируемой программы isnsd
с использованием GDB. Он интегрируется с системой сборки Meson/Ninja через задачу Ninja Build
.
{
"name": "Debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/builddir/isnsd",
"args": [
"-f"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/builddir",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "Ninja Build",
"miDebuggerPath": "/usr/bin/gdb"
}
-
name
:"Debug"
- Имя конфигурации отладки, отображаемое в интерфейсе VS Code (например, в выпадающем меню выбора конфигурации отладки).
-
type
:"cppdbg"
- Тип отладчика, используемый для этой конфигурации. Значение
"cppdbg"
указывает на расширение Microsoft C/C++ для VS Code, которое поддерживает отладку C/C++ приложений через GDB или LLDB. В данном случае используется GDB (см. полеMIMode
).
- Тип отладчика, используемый для этой конфигурации. Значение
-
request
:"launch"
- Тип запроса отладки. Значение
"launch"
означает, что VS Code запустит указанную программу и присоединит к ней отладчик.
- Тип запроса отладки. Значение
-
program
:"${workspaceFolder}/builddir/isnsd"
- Путь к исполняемому файлу, который будет отлаживаться. Переменная
${workspaceFolder}
заменяется на путь к корневой папке проекта в VS Code. В данном случае программа —isnsd
, расположенная в директорииbuilddir
после компиляции.
- Путь к исполняемому файлу, который будет отлаживаться. Переменная
-
args
:["-f"]
- Массив аргументов командной строки, передаваемых программе при её запуске. В данном случае программа
isnsd
запускается с флагом-f
. Этот флаг указывает на работу в foreground-режиме (не в фоновом режиме).
- Массив аргументов командной строки, передаваемых программе при её запуске. В данном случае программа
-
stopAtEntry
:true
- Указывает, должен ли отладчик останавливаться на точке входа программы (обычно в функции
main
). Значениеtrue
означает, что программа начнет выполнение и остановится в начале точки входа программы.
- Указывает, должен ли отладчик останавливаться на точке входа программы (обычно в функции
-
cwd
:"${workspaceFolder}/builddir"
- Рабочая директория (current working directory), в которой будет запущена программа. В данном случае это директория
builddir
в корне проекта. Это важно, если программа ожидает определённые файлы (например, конфигурации) в своей рабочей директории.
- Рабочая директория (current working directory), в которой будет запущена программа. В данном случае это директория
-
environment
:[]
- Массив объектов, задающих переменные окружения для программы. Пустой массив означает, что дополнительные переменные окружения не устанавливаются, и программа использует окружение, унаследованное от VS Code.
-
externalConsole
:false
- Указывает, использовать ли внешнюю консоль для вывода программы. Значение
false
означает, что вывод программы (stdout/stderr) будет отображаться во встроенной консоли VS Code (вкладка “Debug Console”/“Консоль отладки”).
- Указывает, использовать ли внешнюю консоль для вывода программы. Значение
-
MIMode
:"gdb"
- Указывает режим машинного интерфейса (MI, Machine Interface) для отладчика. Значение
"gdb"
означает, что используется отладчик GDB.
- Указывает режим машинного интерфейса (MI, Machine Interface) для отладчика. Значение
-
setupCommands
- Массив команд, которые передаются отладчику GDB перед началом отладки. В данном случае есть одна команда:
{ "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true }
description
: Описание команды для удобства чтения.text
: Команда GDB-enable-pretty-printing
, которая включает улучшенное форматирование вывода структур и объектов в GDB.ignoreFailures
: Еслиtrue
, ошибки выполнения этой команды игнорируются, и отладка продолжается. Это полезно, так как не все версии GDB могут поддерживать эту команду.
- Массив команд, которые передаются отладчику GDB перед началом отладки. В данном случае есть одна команда:
-
preLaunchTask
:"Ninja Build"
- Имя задачи, которая должна быть выполнена перед запуском отладки. В данном случае это задача
"Ninja Build"
, которая определена в файлеtasks.json
.
- Имя задачи, которая должна быть выполнена перед запуском отладки. В данном случае это задача
-
miDebuggerPath
:"/usr/bin/gdb"
- Путь к исполняемому файлу отладчика GDB.
Отладка сборки #
Для отладки достаточно нажать клавишу F5
в VS Code и проект начнет собираться, после чего запустится и остановится на точке входа программы - в функции main
:
В процессе отладки и выполнения шагов по стейтменам (синтаксически завершённая команда) я столкнулся со следущей ошибкой:
** FATAL ERROR **
Error: Error creating pid file /var/run/isnsd.pid: Permission denied
Данная ошибка говорит о том, что выполнение программы запрашивает доступ к файлу /var/run/isnsd.pid
или пути /var/run
. В таком случае подразумевается, что isnsd
требует повышенных привилегий при своей работе. Также isnsd
в процессе работы открывает порт 3205
:
$ sudo lsof -i :3205
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
isnsd 26201 root 4u IPv6 160135 0t0 TCP *:isns (LISTEN)
Повышение привилегий #
Запуск Visual Studio Code с привилегиями отладки может привести к ряду потенциальных проблем: угрозы безопасности, проблемы с файловой системой, проблемы с расширениями и т.п. Но в приведенном выше примере пришлось столкнуться с потребностью в отладке с повышенными привилегиями. Если же к отлаживаемому проекту имеется доверие, то можно реализовать отладку посредством вызова pkexec.
pkexec — это утилита в Linux, позволяющая запускать команды с повышенными привилегиями (обычно от имени root) через PolicyKit (ныне polkit). В отличие от
sudo
,pkexec
предоставляет более гибкую систему авторизации, интегрируясь с графическими средами (например, запрашивает пароль в GUI).
Необходимо создать исполняемый bash скрипт, например /usr/bin/sudo-gdb
:
#!/bin/bash
pkexec /usr/bin/gdb "$@"
При запуске, отладчик GDB принимает переданные скрипту аргументы и вызывается из под вызова pkexec, который в свою очередь запросит ввести пароль, чтобы выполнить запуск GDB от имени суперпользователя:
Таким образом мы запустим отладку лишь авторизовавшись.
Правим в файле launch.json
параметр miDebuggerPath
, содержащий путь к отладчику, на путь к /usr/bin/sudo-gdb
:
"miDebuggerPath": "/usr/bin/sudo-gdb"
Запускаем отладку и получаем запрос на ввод пароля:
После успешной аутентификации отладка запускается в режиме с повышенными привилегиями. Программа isnsd
успешно отлаживается.
Если необходимо убрать постоянный запрос пароля при отладке, то нужно создать разрешающее на запуск GDB с привилегиями правило, например /etc/polkit-1/rules.d/50-allow-gdb.rules
:
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.policykit.exec" &&
action.lookup("program") == "/usr/bin/gdb" &&
subject.user == "<USER>") {
return polkit.Result.YES;
}
});
В качестве <USER>
указать своего пользователя, от имено которого вы работаете в пользовательской среде.
Альтернатива с sudo #
Если же изменить запрос привилегий через sudo в файле /usr/bin/sudo-gdb
#!/bin/bash
sudo /usr/bin/gdb "$@"
то при выполнении отладки мы будем получать ошибки при запросе ввода пароля, так как VS Code не предоставляет интерактивный ввод пароля для консольных утилит:
sudo: unable to read password: Input/output error
В этом случае необходимо явно разрешить выполнение GDB посредством sudo от имени нашего пользователя, добавив правило для sudo, например /etc/sudoers.d/50-allow-gdb
:
<USER> ALL=(ALL) NOPASSWD: /usr/bin/gdb
В качестве <USER>
указать своего пользователя, от имено которого вы работаете в пользовательской среде.
Конечно, многие параметры и разрешения для прочих пользователей и групп - это опциональные настройки. Выше приведены лишь примеры реализации данных разрешений. Выбор подхода зависит лишь от ваших требований, но не стоит забывать о безопасности.