Пост содержит подробное руководство как организовать удаленную отладку разрабатываемого приложения на .NET 5.0 в Visual Studio Code для устройства на ARM процессоре, на устройстве установлена Armbian (Linux). Благодаря кроссплатформенности .NET 5.0, разработанное приложение будет одинаково работать как в Windows, так и в Linux. Но все становится сложнее, если необходимо взаимодействовать с подсистемами Linux. Каждый раз компилировать в Windows и переносить исполняемые файлы ручным способом на Linux не очень удобно. Один из рабочих примеров для подобного решения является задача отладки взаимодействия приложения на C# в Linux с устройством подключенным по протоколу RS232. В качестве платформы запуска будем использовать Cubietruck (ARM32), и Banana Pi BPI-M64(ARM64), работающие на Armbian.
Постановка задачи
В Visual Studio Code под ОС Windows x64 разрабатываем проект для ARM. Отладка приложения будет состоять из трех задач:
- компиляция под архитектуру ARM
- копирование исполняемых файлов на конечное устройство
- запуск удаленного отладчика на конечном устройстве.
Компиляция под архитектуру ARM будет выполняться с помощью .NET SDK, командой: dotnet build . Копирование файлов будем производить программой Rsync. В качестве pipeTransport для связи с удаленной системой будет использоваться программа PLINK, из пакета PuTTY. Удаленный отладчик от Microsoft — vsdbg.
Предварительные требования
- одноплатный компьютер на ARM процессоре не ниже ARMv7.
- ОС Linux на плате ARM с ядром Linux 5.8 (с более старыми ядрами не проводилась проверка)
- Локальный компьютер для разработки с Visual Studio Code, работающий под управлением Windows 7+ x64, с установленным расширением C# for Visual Studio Code (powered by OmniSharp) и SDK .NET
Тестирование производилось на следующие устройствах:
- Плата Cubietruck установлена версия Armbian_21.02.1_Cubietruck_focal_current_5.10.12.img.xz, основанная на Ubuntu 20.04.1 LTS (Focal Fossa), ядро Linux 5.10.12. uname: Linux cubietruck 5.10.12-sunxi #21.02.1 SMP Wed Feb 3 20:39:30 CET 2021 armv7l armv7l armv7l GNU/Linux.
- Плата Banana Pi BPI-M64 установлена версия Armbian_20.08.2_Bananapim64_bionic_current_5.8.6_minimal.img.xz, основанная на Ubuntu 18.04.5 LTS (Bionic Beaver), ядро Linux 5.8.6. uname: Linux bananapim64 5.8.6-sunxi64 #20.08.2 SMP Fri Sep 4 08:52:31 CEST 2020 aarch64 aarch64 aarch64 GNU/Linux.
Шаг 1 — Установка SDK .NET, Visual Studio Code и расширение поддержки C# в Windows
Установка SDK .NET, Visual Studio Code и расширение поддержки C# в Windows подробно описана в посте Установка .NET 5.0 для ARM на примере Banana Pi BPI-M64 и Cubietruck (Armbian, Linux) и Создание первого приложения на .NET 5.0 в Visual Studio Code для ARM.
Шаг 2 — Установка программ MobaXterm, cwRsync и PuTTY, в Windows
MobaXterm
Терминал MobaXterm — существенно удобнее в работе по сравнению с PuTTY терминалом, и позволяет выполнять загрузку/копирование файлов и папок в один клик. Загрузим по ссылке и установим.
cwRsync
Для копирования сборок и файлов проекта будем использовать программу Rsync. Rsync — программа для UNIX-подобных систем, которая эффективно выполняет синхронизацию файлов и каталогов в двух местах (необязательно локальных) с минимизированием трафика, используя кодирование данных при необходимости. Важным отличием rsync от многих других программ/протоколов является то, что зеркалирование осуществляется одним потоком в каждом направлении (а не по одному или несколько потоков на каждый файл). rsync может копировать или отображать содержимое каталога и копировать файлы, опционально используя сжатие и рекурсию. rsync передаёт только изменения файлов, что отражается на производительности программы.
Но Rsync не предназначена для платформы Windows, поэтому воспользуемся пакетом cwRsync в который входит Rsync для Windows.
cwRsync — это пакет, состоящий из графической оболочки (начиная с версии 5), утилиты Rsync и библиотеки Cygwin. Пакет cwRsync позволяет организовать удалённое резервное копирование и синхронизацию файлов между серверами Windows. Также с помощью cwRsync можно осуществлять резервное копирование файлов Unix сервера на сервер Windows и наоборот. Конфигурирование Rsync под Windows в пакете cwRsync отличается от Unix только указанием путей к каталогам для синхронизации: пути Windows стиля Диск:\путь преобразуются (по правилам cygwin) в /cygdrive/диск/путь/, например, c:\windows нужно указывать как /cygdrive/c/windows.
Создадим папку C:\RemoteCode\cwrsync и распакуем в нее содержимое пакета cwrsync_6.2.0_x64_free.zip, так что бы программа rsync.exe была доступна по пути c:\RemoteCode\cwrsync\rsync.exe
PuTTY
Загрузим PuTTY в виде putty.zip(x64) и распакуем пакет, так что бы программа PUTTY.EXE была доступна по пути c:\RemoteCode\putty\PUTTY.EXE
Шаг 3 — Настройка удаленного входа под root для SSH
Более подробнее можно почитать в руководстве Использование SSH для подключения к удаленному серверу
Для доступа пользователя root по SSH необходимо внести изменения в конфигурационный файл /etc/ssh/sshd_config в Ubuntu. Войдем в систему и установим Midnight Commander в состав которого входит редактор mcedit:
$ sudo apt-get update $ sudo apt-get install -y openssh-server mc $ sudo mcedit /etc/ssh/sshd_config
Удалим строчку #PermitRootLogin prohibit-password и заменим на строку PermitRootLogin yes. Сохраним изменение в файле кнопка «F2» и выйдем из редактора «F10».
Перезагрузим сервер sshd, чтобы изменения вступили в силу, выполним команду:
$ sudo systemctl reload ssh
Теперь можно зайти по SSH на Armbian под пользователем root используя терминал MobaXterm.
Шаг 3 — Настройка ключей аутентификации для доступа SSH в Armbian
Более подробнее можно почитать в руководстве Как настроить ключи SSH в Ubuntu 18.04.
Ключ доступа необходим для избежания хранения пароля root пользователя в открытом виде в конфигурационном файле проекта Visual Studio Code, т.к. все проекты хранятся на корпоративном Git-сервере, и утечка пароля нежелательна. Аутентификация с помощью ключей реализуется путем создания пары ключей: приватного ключа и публичного ключа.
Приватный ключ располагается на клиентском компьютере, этот ключ защищен и хранится в секрете.
Публичный ключ может передаваться любому лицу или размещаться на сервере, доступ к которому вы хотите получить.
Приватный и публичный ключ сгенерируем на плате в Armbian, затем скопируем себе приватный ключ в Windows, и удалим этот ключ в Armbian.
Используя терминал MobaXterm выполним следующую команду, для генерации пары ключей:
$ ssh-keygen -t rsa
Нажмите ENTER, чтобы принять используемые по умолчанию значения. Пароль для ключа устанавливать не требуется, нажать ENTER:
root@bananapim64:~# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:xayvVRQvFSxNCiLl1zwqmtfhAn+sMcU7oDCI7SAzQJ0 root@bananapim64 The key's randomart image is: +---[RSA 2048]----+ | .. . ..o . .++. | |. E o + +.=o | |. . = B.. | |.o . = o o | |* o o . S = . | |.= o * O + | | . + * X | | . O . | | o | +----[SHA256]-----+ root@bananapim64:~#
По результату выполнения команды в каталоге ~/.ssh будут сгенерированы два ключа:
- /id_rsa.pub — публичный ключ, остается на Armbian
- /id_rsa — приватный ключ, копируем на компьютер с Windows в папку C:\RemoteCode , и удаляем этот файл в Armbian.
Теперь необходимо прописать публичный ключ в системе для доступа по SSH под пользователем root, для этого необходимо внести содержимое файла id_rsa.pub в файл ~/.ssh/authorized_keys , выполним следующие команды для этого:
$ sudo touch ~/.ssh/authorized_keys $ sudo cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys $ sudo systemctl reload ssh
Теперь необходимо проверить доступность с помощью PuTTY, который был ранее загружен в папку c:\RemoteCode\putty . Для использование ключа id_rsa необходимо его конвертировать из старого формата PEM с помощью программы c:\RemoteCode\putty\PUTTYGEN.EXE . Запускаем программу PUTTYGEN.EXE ,выбираем пункт меню «Conversions», затем нажимаем на пункт«Import key», после чего выбираем ключ c:\RemoteCode\id_rsa . Для конвертации ключа после импорта нажимаем на кнопку «Save private key». Пароль для ключа не задаем, поэтому подтверждаем сохранение ключа без пароля, отвечаем: Да. Сохраняем файл с в новом формате (.ppk), путь c:\RemoteCode\id_rsa.ppk .
Проверим подключение к Armbian с использованием ключа id_rsa.ppk при помощи программы c:\RemoteCode\putty\PLINK.EXE , запустим командную строку в Windows и выполним, где 192.168.43.225 — IP-адрес компьютера на Armbian, и введем пароль входа в учетную запись root:
C:\RemoteCode\putty\PLINK.EXE -ssh root@192.168.43.225 -pw exit
После выполнение этой команды потребуется согласиться с хэшированием ключа удаленного компьютера, если этого не сделать, то потом возникнут проблемы при удаленном подключение по SSH, после выполнения команды закрыть окно терминала.
Затем снова запустить консольное окно и выполнить команду:
C:\RemoteCode\putty\PLINK.EXE -i c:\RemoteCode\id_rsa.ppk root@192.168.43.225 -batch -T echo "hello world"
В результате мы должны увидеть: «hello world». Подключение настроено.
Шаг 4 — Создание консольного приложения и добавление логики
Создадим папку c:\RemoteCode\Projects\ , перейдем в нее и запустим командную строку. В командной стоке выполним команду: dotnet new console -o RemoteAppArm64 , где RemoteAppArm64 — название нового проекта для ARM64, проект для ARM32 будет называться — RemoteAppArm.
Теперь необходимо добавить дополнительную логику в новое приложение. Приложение будет выводить информацию о системе, в которой оно работает. Запустим Visual Studio Code и откроем папку с проектом c:\RemoteCode\Projects\RemoteAppArm64\ .
В функцию Main() поместим следующий код с вызовом исключения для проверки режима отладки, на GitHub версия проекта для ARM64 доступна по ссылке RemoteAppArm64, версия для ARM32 RemoteAppArm:
static void Main(string[] args)
{
Console.WriteLine("Test .NET console application!");
var str_Framework=RuntimeInformation.FrameworkDescription;
var str_OSArch=RuntimeInformation.OSArchitecture.ToString();
var str_OSDesc= RuntimeInformation.OSDescription;
var str_OSIdent=RuntimeInformation.RuntimeIdentifier;
//output
Console.WriteLine($"Версия .NET: {str_Framework}");
Console.WriteLine($"Архитектура ОС: {str_OSArch}");
Console.WriteLine($"Версия ОС: {str_OSDesc}");
Console.WriteLine($"Идентификатор ОС: {str_OSIdent}");
//error
Console.WriteLine("Создание исключения");
throw new Exception("А вот и ошибочка!");
Console.WriteLine("Завершение работы программы");
}
Сохраним изменения. Далее, откроем меню Terminal => New Terminal:
Укажем команду сборки проекта: dotnet build
Затем команду для запуска приложения: dotnet run
После запуска, приложение вылетит с ошибкой.
Теперь запустим отладку, меню Run => Start Debugging, будет вызвано исключение.
В разделе Run => VARIABLES => Locals можно посмотреть текущее состояние переменных, так же при наведение указателя на переменную в коде, всплывает подсказка с содержанием переменной.
Отладка в Windows успешно работает!
Шаг 5 — Установка удаленного отладчика vsdbg в Armbian
Установка отладчика vsdbg
Установка vsdbg достаточно простая, при установке обязательно необходимо обратить внимание на архитектуру выбора отладчика, исходя из Runtime Identifiers (RIDs), для нас требуется значение: linux-arm и linux-arm64.
Выполним команду в Armbian, где linux-arm, linux-arm64 — архитектура платформы, в результате будет установлен отладчик vsdbg по пути /usr/share/vsdbg :
$ mkdir -p /usr/share/vsdbg $ curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -r linux-arm64 -v latest -l /usr/share/vsdbg
$ mkdir -p /usr/share/vsdbg $ curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -r linux-arm -v latest -l /usr/share/vsdbg
Результат выполнения:
root@bananapim64:~# mkdir -p /usr/share/vsdbg
root@bananapim64:~# curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -r linux-arm64 -v latest -l /usr/share/vsdbg
Info: Previous installation at '/usr/share/vsdbg' not found
Info: Using vsdbg version '16.9.20122.2'
Using arguments
Version : 'latest'
Location : '/usr/share/vsdbg'
SkipDownloads : 'false'
LaunchVsDbgAfter : 'false'
RemoveExistingOnUpgrade : 'false'
Info: Using Runtime ID 'linux-arm64'
Downloading https://vsdebugger.azureedge.net/vsdbg-16-9-20122-2/vsdbg-linux-arm64.tar.gz
Info: Successfully installed vsdbg at '/usr/share/vsdbg'
root@cubietruck:~# mkdir -p /usr/share/vsdbg
root@cubietruck:~# curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -r linux-arm -v latest -l /usr/share/vsdbg
Info: Previous installation at '/usr/share/vsdbg' not found
Info: Using vsdbg version '16.9.20122.2'
Using arguments
Version : 'latest'
Location : '/usr/share/vsdbg'
SkipDownloads : 'false'
LaunchVsDbgAfter : 'false'
RemoveExistingOnUpgrade : 'false'
Info: Using Runtime ID 'linux-arm'
Downloading https://vsdebugger.azureedge.net/vsdbg-16-9-20122-2/vsdbg-linux-arm.tar.gz
Info: Successfully installed vsdbg at '/usr/share/vsdbg'
Проверить работу отладчика и узнать параметры запуска можно командой: vsdbg -?
root@bananapim64:/usr/share/vsdbg# ./vsdbg -?
Microsoft .NET Core Debugger (vsdbg)
Copyright (C) Microsoft Corporation. All rights reserved.
Options:
--interpreter=vscode Standard input and output will contain
Debug Adapter Protocol commands. This is currently the only supported
interpreter mode and it is used for all Visual Studio products.
--pauseEngineForDebugger Wait for a debugger to attach during startup
--engineLogging[=] Enable logging to VsDbg-UI or file for the engine.
--elapsedTiming Include elapsed timing in engine logging.
--connection= Connect to the specified VsDbg-UI instance.
--heartbeat= Send a heartbeat message with given period (in
seconds).
--tty=
Шаг 6 — Конфигурирование launch.json и tasks.json для удаленной отладки
Все подготовительные шаги для удаленной отладки выполнены, остается внести изменения в конфигурацию проекта. Начнем с задач, файл tasks.json содержит выполняемые задачи. Для удаленной отладки необходимо последовательно выполнить две задачи: скомпилировать проект для Linux ARM, скопировать полученные файлы на плату с Armbian, и в конечном итоге запустить удаленный отладчик.
По умолчанию в файле tasks.json определены три задачи: build, publish и watch. Эти три задачи изменять не будем, создадим новую задачу build-linux-arm на основе задачи build. И задачу copy-to-device, которая будет запускать программу rsync для копирование файлов.
На GitHub версия проекта для ARM64 доступна по ссылке RemoteAppArm64, версия для ARM32 RemoteAppArm.
Файл tasks.json, задача build-linux-arm:
{
"label": "build-linux-arm",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/RemoteAppArm64.csproj",
"--configuration","Debug",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"-r","linux-arm64"
],
"problemMatcher": "$msCompile"
}
{
"label": "build-linux-arm",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/RemoteAppArm.csproj",
"--configuration","Debug",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"-r","linux-arm"
],
"problemMatcher": "$msCompile"
}
Добавляем параметры:
- «—configuration»,»Debug» — для включения отладки;
- «-r»,»linux-arm64″ — для компиляции под архитектуру ARM.
Файл tasks.json, задача copy-to-device
{
"label": "copy-to-device",
"dependsOn":"build-linux-arm",
"command": "C:\\RemoteCode\\cwrsync\\rsync.exe",
"type": "process",
"args": [
"--log-file=rsync.log",
"--progress",
"-avz" ,
"-e",
"C:\\RemoteCode\\cwrsync\\ssh.exe -i C:\\RemoteCode\\id_rsa -o 'StrictHostKeyChecking no'",
"/cygdrive/c/RemoteCode/Projects/RemoteAppArm64/bin/Debug/net5.0/linux-arm64/",
"root@192.168.43.225:/root/RemoteAppArm64"
],
"problemMatcher": "$msCompile"
}
{
"label": "copy-to-device",
"dependsOn":"build-linux-arm",
"command": "C:\\RemoteCode\\cwrsync\\rsync.exe",
"type": "process",
"args": [
"--log-file=rsync.log",
"--progress",
"-avz" ,
"-e",
"C:\\RemoteCode\\cwrsync\\ssh.exe -i C:\\RemoteCode\\id_rsa -o 'StrictHostKeyChecking no'",
"/cygdrive/c/RemoteCode/Projects/RemoteAppArm/bin/Debug/net5.0/linux-arm/",
"root@192.168.43.12:/root/RemoteAppArm"
],
"problemMatcher": "$msCompile"
}
Описание параметров:
- «dependsOn»:»build-linux-arm» — зависимость от задачи build-linux-arm, задача copy-to-device выполнится только после задачи build-linux-arm. Параметр dependsOn позволяет указывать несколько задач, но в этом случае они будут выполняться параллельно. Если требуется выполнять последовательно три задачи, то тогда следует в задачу task3 добавить параметр «dependsOn»:»task2″, а в задачу task2 параметр «dependsOn»:»task1″.
- «command»: «c:\\RemoteCode\\cwrsync\\rsync.exe» — запускаемая программа rsync.exe для копирования файлов раздел «args» — содержит список аргументов передаваемые программе rsync.exe.
- «—log-file=rsync.log» — включим журналирование в файл rsync.log, для проверки какие файлы были скопированы.
- «—progress» — визуализация процесса копирования файлов в режиме отладки
- «c:\\RemoteCode\\cwrsync\\ssh.exe -i c:\\RemoteCode\\id_rsa», — приватный ключ доступа к плате ARM на Armbian
- «/cygdrive/c/RemoteCode/Projects/RemoteAppArm64/bin/Debug/net5.0/linux-arm64/» — папка со сборками и файлами, которые необходимо скопировать на плату ARM
- «root@192.168.43.225:/root/RemoteAppArm64» — Сборки и файлы будут скопирована по пути /root/RemoteAppArm64 на плату ARM с IP-адресом 192.168.43.225. На плате ARM64 потребуется создать папку по пути /root/RemoteAppArm64 , на ARM32 — /root/RemoteAppArm !
Создание папки для запускаемых файлов на плате:
$ mkdir -p ~/RemoteAppArm64
$ mkdir -p ~/RemoteAppArm
Файл launch.json. Содержание текущего файла полностью удалим и заменим на представленный вариант:
"configurations": [
{
"name": ".NET Core Remote Launch - Framework Dependent (console)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": ["./RemoteAppArm64.dll"],
"cwd": "~/RemoteAppArm64",
"stopAtEntry": false,
"console": "internalConsole",
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "C:\\RemoteCode\\putty\\PLINK.EXE",
"pipeArgs": [
"-i",
"C:\\RemoteCode\\id_rsa.ppk",
"root@192.168.43.225"
],
"debuggerPath": "/usr/share/vsdbg/vsdbg --engineLogging=/var/log/vsdbg.log"
},
"preLaunchTask": "copy-to-device",
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
"configurations": [
{
"name": ".NET Core Remote Launch - Framework Dependent (console)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": ["./RemoteAppArm.dll"],
"cwd": "~/RemoteAppArm",
"stopAtEntry": false,
"console": "internalConsole",
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "C:\\RemoteCode\\putty\\PLINK.EXE",
"pipeArgs": [
"-i",
"C:\\RemoteCode\\id_rsa.ppk",
"root@192.168.43.12"
],
"debuggerPath": "/usr/share/vsdbg/vsdbg --engineLogging=/var/log/vsdbg.log"
},
"preLaunchTask": "copy-to-device",
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
- «args»: [«./RemoteAppArm64.dll»] — запускаемая сборка RemoteAppArm64.dll
- «cwd»: «~/СonsoleAppUbuntu» — папка ~/СonsoleAppUbuntu с бинарными файлами для запуска
- «pipeTransport» — раздел для связи отладчика используя транспорт в виде программы PLINK.EXE , указываем ключ и IP-адрес подключения
- «debuggerPath»: «~/vsdbg/vsdbg» — путь с программе отладчика на плате в Armbian
- «preLaunchTask»: «copy-to-device» — до выполнения отладки необходимо выполнить задачу copy-to-device, которая соберет проект и выполнить копирование его на плату Arm под Armbian.
Сохраним все изменения, и перейдем к следующему шагу.
Шаг 7 — Проверка удаленной отладки путем вызова исключения.
Если вы все верно выполнили на предыдущих шагах, то после запуска Run => Start Debugging, произойдет компиляция проекта под архитектуру ARM32/ARM64, затем результат будет скопирован на отладочную плату ARM. Для проверки какие файлы были скопированы посмотрите файл rsync.log в папке проекта. Если в файле будет указана сборка включая /RemoteAppArm64.dll или /RemoteAppArm.dll , значит все отлично, и остается последний шаг это связь c отладчиком. После запуска приложения в Visual Studio Code вы увидите:
Удаленная отладка на ARM работает!
Проекты на GitHub — Remote-Debugging-with-VS-Code-On-Windows-to-a-ARM-using—NET
Литература
- Debug .NET Core on Linux using SSH by attaching to a process — Visual Studio Docs
- Remote Debugging On Linux Arm — omnisharp-vscode
- Debug .NET apps on Raspberry Pi — .NET IoT Libraries
- Remote debugging with VS Code on Windows to a Raspberry Pi using .NET Core on ARM — SCOTT HANSELMAN
- Attaching to remote processes — omnisharp-vscode
- Offroad Debugging of .NET Core on Linux OSX from Visual Studio — microsoft/MIEngine
- Remote Debugging of a .Net Core application with VS Code on PLCnext — KAY SUTTKUS Makers Blog
- Tasks (legacy version) — Visual Studio Code
- How to chain tasks in Visual Studio Code using only tasks.json? — stackoverflow
- Debugging — Visual Studio Code
- Visual Studio Code remote debugging of a .Net Core application running on Raspberry Pi and Ubuntu Linux — jenx.si
- Как настроить ключи SSH в Ubuntu 18.04 — DigitalOcean
- How To Use Rsync to Sync Local and Remote Directories — DigitalOcean
- Using “preLaunchTasks” and Naming a Task in Visual Studio Code — stackoverflow
- PuTTY выдает ошибку Unable to use key file — Atlex
- windows rsync from different local drive, other than c, to remote — serverfault





