Хотел сперва задать свой вопрос-размышление о том, использовать ли в драйверах фичи C++, называть ли файлы .cpp и .hpp или .c и .h или .cpp и .h.
Но сперва задался более важным и фундаментальным вопросом и вы его читаете.
Итак, юнит-тесты для драйверов.
Во-первых, в драйверах все-таки приходится писать такие вещи, как: какая-то функция-хелпер, класс-хелпер, своя реализация чего-то там. Насколько часто — это вопрос, но даже в сэмплах от MS на гитхабе мы видим тому примеры.
Все это надо покрывать юнит-тестами, т.е. писать синтетический код, который будет вызывать конкретные функции и классы с разными параметрами.
И даже вызывать одно и то же по много раз. Не знаю, как это называется — может быть подойдет понятие stress testing. Но штука полезная, в частности для проверки на утечки памяти и на быстродействие.
Основная же суть тестирования просто в том, чтобы, внеся какое-то изменение в покрытый код, можно было убедиться в полном соответствии всем ранее выставленным требованиям. Причем юнит-тесты позволяют начать убеждаться в 2 клика и уже в первые доли секунды
Во-вторых, думаю, что хорошо бы и для именно NT-шных вещей, таких, как каллбеки минифильтров, тоже писать юнит-тесты. Но это уже намного сложнее — там слишком много специфики, которую в юзермоде сложно вызвать.
Оба случая, особенно второй, требует наличие какой-то системы, ну как минимум в какой IDE это делать, на каком языке (C++ или Си) и в каком виде.
Что кто использует?
Конечно, хотелось бы тестить в юзер-моде с песочницей, а не в режиме ядра. Но думаю это вообще несбыточно. А если написать, то юзать будут 1.5 человека со всей планеты, включая меня.
Здравствуйте, LimyKurn, Вы писали:
LK>Конечно, хотелось бы тестить в юзер-моде с песочницей, а не в режиме ядра. Но думаю это вообще несбыточно. А если написать, то юзать будут 1.5 человека со всей планеты, включая меня.
Здравствуйте, LimyKurn, Вы писали:
LK>Итак, юнит-тесты для драйверов. LK>... LK>Оба случая, особенно второй, требует наличие какой-то системы, ну как минимум в какой IDE это делать, на каком языке (C++ или Си) и в каком виде. LK>Что кто использует?
Лично я сторонних подхода, что драйверный код можно и нужно тестировать и отлаживать в режиме пользователя.
Любые системные функции и компоненты можно "замокать" (т.е. запрограммировать соответствующие прокси,
эмулирующие нужное поведение). Некоторые сценарии, например отказ записи на диск при нехватке на нем места,
вообще можно тестировать только так. Не будешь же на самом деле ради одного мелкого теста забивать весь
диск мусором, чтобы проверить.
Представь, что нужно покрыть тестами код minifilter калбэка, который, скажем, блокирует изменение
атрибутов безопасности файла или папки. Чтобы проверить это "в бою", нужно запустить виртуальную машину,
перевести ее в тестовый режим, проинсталлировать туда свой драйвер, приаттачить его к нужному тому,
затем с помощью тестового приложения, которое, кстати, еще придется написать, вызвать соответствующую
функцию, обработать результат и каким-то образом сообщить "наверх" о том, пройден ли тест или провален.
Да, а еще предусмотреть зависание виртуалки или выпадание ее в BSOD... Жуть.
Я использую иной подход, который более в духе концепций юнит-тестирования: код калбэка переносится в
user mode (обычный проект C++ в Visual Studio), все недостающие функции ядра заменяются своими "прокси",
где программируется нужное поведение, после этого остается только написать код, который вызывает этот
callback с различными входными параметрами и проверяет, что на выходе. Опять же, большой плюс то, что
можно тестировать самые разнообразные сценарии, поймать или сэмулировать которые в реальных условиях
затруднительно или вообще практически невозможно.
Таким же способом тестируется вся библиотечная и алгоритмическая составляющая часть проекта, даже
самые простейшие вещи. Например, есть класс AutoSpinLock, который захватывает спинлок в конструкторе и
освобождает его в деструкторе (RAII). Ну как его лучше всего протестировать? Не писать же ради теста
целый драйвер, который в одном потоке захватывает и освобождает спинлок, а в другом проверяет, занят ли
спинлок или доступен. Нет. Пишется код с "заглушками" для KeAcquireSpinLock/KeReleaseSpinLock, которые
фиксируют просто сам факт их вызова, а тест в нужных точках проверяет, были ли вызваны нужные
функции или нет. Простейший пример:
//
// Mocks.
//
VOID KeAcquireSpinLock(PKSPIN_LOCK lock, IRQL * irql)
{
++g_acquireCount;
}
VOID KeReleaseSpinLock(PKSPIN_LOCK lock, IRQL irql)
{
++g_releaseCount;
}
//
// Tests.
//void testAutoSpinLock()
{
BEGIN_TEST(testAutoSpinLock);
ASSERT(0 == g_acquireCount);
ASSERT(0 == g_releaseCount);
SpinLock spinLock;
{
AutoSpinLock lock(spinLock);
ASSERT(1 == g_acquireCount);
ASSERT(0 == g_releaseCount);
}
ASSERT(1 == g_acquireCount);
ASSERT(1 == g_releaseCount);
//
// в этот тест еще вставляются проверки, что правильно изменяется и восстанавливается
// оригинальный IRQL, что указатель на KSPIN_LOCK правильный и т.д.
//
END_TEST();
}
Код тестов собирается из исходников драйвера в отдельный проект и может запускаться
прямо где-нибудь в build events, сразу сообщая о любых неполадках.
----
Другая фаза — функциональные и прочие тесты. Вот там уже приходится писать всякие тестовые
запускалки, проверяющие поведение в реальных условиях. Думаю, здесь логичнее использовать
вообще не C/C++, а что-то более легковесное, например python.
Re[2]: Кто как пишет юнит-тесты для драйверов NT + Моя идея песочни
Здравствуйте, -prus-, Вы писали:
P>Здравствуйте, LimyKurn, Вы писали:
LK>>Конечно, хотелось бы тестить в юзер-моде с песочницей, а не в режиме ядра. Но думаю это вообще несбыточно. А если написать, то юзать будут 1.5 человека со всей планеты, включая меня.
P>Testing a Driver. Не?
Изучим, спасибо. А там точно юзер-мод? И как быть, если у меня Windows Driver Kit 7.1, а код пишется в блокноте? Я понимаю всю порочность этой системы, но тем не менее уже есть такой проект и переделывать его никто не просит = согласия такого не дает
Re[2]: Кто как пишет юнит-тесты для драйверов NT + Моя идея
Здравствуйте, okman, Вы писали:
O>Здравствуйте, LimyKurn, Вы писали:
O>Я использую иной подход, который более в духе концепций юнит-тестирования: код калбэка переносится в O>user mode (обычный проект C++ в Visual Studio), все недостающие функции ядра заменяются своими "прокси"
То есть можно считать, что ты уже начал писать такую систему, т.к. что-то там у тебя есть? Я тоже начал, под конкретный драйвер.
Первое, что я сделал, это реализовал ExAllocatePoolWithTag на malloc, ExFreePoolWithTag на free, и макрос для проверки на утечки. Планируется полноценная песочница для памяти и нормальное профилирование для поиска утечек.
Объединиться бы нам как-то, что ли.
Только вот сильно беспокоит финансовая часть проекта. Я, конечно, буду и сам иногда писать какие-то драйвера. Но вот работать у Касперского, Аваста, вообще в команде — желания нет. А вот если мою систему оценят разработчики Касперского, Аваста, или хоть чего помельче, то буду искренне рад принести им пользу и получить за это заслуженное вознаграждение. Даже если они просто напишут: "отличная идея, скорее делайте и заливайте на гитхаб, ждем с нетерпением", как мне ответили в одной американской фирме на предложение написать для них некую библиотеку, то это уже что-то.
Соответственно, следует как-то поговорить с такими ребятами — что они думают об идее песочницы.
Но и если песочницу отвергнут, то, в принципе, можно и так: O>Представь, что нужно покрыть тестами код minifilter калбэка, который, скажем, блокирует изменение O>атрибутов безопасности файла или папки. Чтобы проверить это "в бою", нужно запустить виртуальную машину, O>перевести ее в тестовый режим, проинсталлировать туда свой драйвер, приаттачить его к нужному тому, O>затем с помощью тестового приложения, которое, кстати, еще придется написать, вызвать соответствующую O>функцию, обработать результат и каким-то образом сообщить "наверх" о том, пройден ли тест или провален. O>Да, а еще предусмотреть зависание виртуалки или выпадание ее в BSOD... Жуть.
Если его один раз сделать, то дальше уже не такая жуть будет.
Ну и да, по ссылке выше от -prus- тоже надо посмотреть.
А вот если вообще никому эти тесты толком не нужны — тогда увы
Немного технической информации:
Если все-таки песочница, то я ее начал делать не используя Visual Studio, а используя лишь сам WinDDK. Но эта идея оказалась несостоятельной — очень не хватает уже можно догадаться чего — std::vector и std::string :D
Второй момент — не хватает плюшек по синтаксису вроде лямбд.
Так что VS в этом плане рулит. Хотя по-хорошему и сами драйвера надо в ней писать — а у нас вот проект в WinDDK 7.1, собирается батниками, а пишется в блокноте, никакой VS — оттуда и возникла порочная идея юзать такую же систему для тестов.
O>Другая фаза — функциональные и прочие тесты. Вот там уже приходится писать всякие тестовые O>запускалки, проверяющие поведение в реальных условиях. Думаю, здесь логичнее использовать O>вообще не C/C++, а что-то более легковесное, например python.
Я это вручную делаю пока. Написана тестовая утилита на C#, но она не автоматическая — надо жать на кнопки в окошке. И результат она не анализирует — его надо смотреть самому.
Здравствуйте, LimyKurn, Вы писали:
LK>То есть можно считать, что ты уже начал писать такую систему, т.к. что-то там у тебя есть?
Да, я давно уже пишу драйверные тесты в таком ключе.
LK>Объединиться бы нам как-то, что ли. LK>Только вот сильно беспокоит финансовая часть проекта. Я, конечно, буду и сам иногда писать какие-то драйвера. Но вот работать у Касперского, Аваста, вообще в команде — желания нет. А вот если мою систему оценят разработчики Касперского, Аваста, или хоть чего помельче, то буду искренне рад принести им пользу и получить за это заслуженное вознаграждение. Даже если они просто напишут: "отличная идея, скорее делайте и заливайте на гитхаб, ждем с нетерпением", как мне ответили в одной американской фирме на предложение написать для них некую библиотеку, то это уже что-то. LK>Соответственно, следует как-то поговорить с такими ребятами — что они думают об идее песочницы.
У меня нет цели разрабатывать какую-то обобщенную мега-систему для тестирования драйверов и тем более пытаться
куда-то ее продавать. Я просто пишу тесты под драйверы, которые разрабатываю или поддерживаю.
LK>Немного технической информации: LK>Если все-таки песочница, то я ее начал делать не используя Visual Studio, а используя лишь сам WinDDK. Но эта идея оказалась несостоятельной — очень не хватает уже можно догадаться чего — std::vector и std::string :D LK>Второй момент — не хватает плюшек по синтаксису вроде лямбд. LK>Так что VS в этом плане рулит. Хотя по-хорошему и сами драйвера надо в ней писать — а у нас вот проект в WinDDK 7.1, собирается батниками, а пишется в блокноте, никакой VS — оттуда и возникла порочная идея юзать такую же систему для тестов.
Но никто же не мешает сами драйверы компилить WinDDK 7.1, а тесты для них писать на VS2017, используя самые последние версии C++.
Re[3]: Кто как пишет юнит-тесты для драйверов NT + Моя идея
Здравствуйте, Вы писали:
LK>А вот если мою систему оценят разработчики Касперского, Аваста, или хоть чего помельче, то буду искренне рад принести им пользу и получить за это заслуженное вознаграждение. Даже если они просто напишут: "отличная идея, скорее делайте и заливайте на гитхаб, ждем с нетерпением", как мне ответили в одной американской фирме на предложение написать для них некую библиотеку, то это уже что-то. LK>Соответственно, следует как-то поговорить с такими ребятами — что они думают об идее песочницы.
Я готов обсуждать песочницу и даже поучаствовать в работе. Если есть желание пообщаться, можно написать мне на andrey.sobko@kaspersky.com
Re: Кто как пишет юнит-тесты для драйверов NT + Моя идея песочни
Я когдато в ресерческих целях делал следующий фокус-покус:
Обычная юзер-мод апликуха, которая загружала кернелмодный драйвер при помощи ZwMapViewOfSection(SEC_IMAGE) и настраивала всего его IAT на свои заглушки. Оно даже работало. Вопрос был в реализации заглушек.
Зачем? Исследовалась возможность поднять arbitrary NDIS стек в юзермоде, для получения полного сетевого стека внутри user-mode app virtualization, с получением виртуального сетевого адаптера и пробросом его пакетов через VPN. Результат исследования: это возможно, но проще тупо обхукать винсок
Ну а для тестов как okman писал драйвер подымать незачем. Достаточно просто писать отрываемый от системного АПИ код, и сажать его на моки.
Как много веселых ребят, и все делают велосипед...
Re[4]: Кто как пишет юнит-тесты для драйверов NT + Моя идея
Здравствуйте, okman, Вы писали:
O>У меня нет цели разрабатывать какую-то обобщенную мега-систему для тестирования драйверов и тем более пытаться O>куда-то ее продавать. Я просто пишу тесты под драйверы, которые разрабатываю или поддерживаю.
А можно взглянуть на тесты? Точнее, конкретно на "прокси" для функций.
Если не хочешь продавать, то, может быть, попрошу тебя собрать эти прокси воедино и продокументировать бесплатно
O>Но никто же не мешает сами драйверы компилить WinDDK 7.1, а тесты для них писать на VS2017, используя самые последние версии C++.
Возможно, и в самих драйверах тоже найдется место C++.
Из C++ многого не хватает, потому я решил в драйвере, который поддерживаю, оставить авторский стиль: код делать .cpp, а заголовки .h, чтобы заголовки можно было использовать и из .cpp, и из .c, а на чем реализация — это уже на усмотрение программиста. (В то же время, я сохраняю часть правил Си, например переменные реально лучше объявлять в начале кода, чтобы знать, что если все эти переменные корректно очищаются, то нигде не может быть утечек.)
Правда, драйвер как-то слишком долго думает, и я попробую переписать его на Си.
Но если окажется, что нужен C++, то выйдет, что C++ нужен еще и новый.
Отсюда возникла идея перейти на новый WinDDK (полагаю, там C++ вплоть до 17), ну а VS для тестов и самих драйверов тоже — это само собой.
До какой версии можно WinDDK, если драйвер должен быть совместим с WinXP?
Здравствуйте, ononim, Вы писали:
O>Ну а для тестов как okman писал драйвер подымать незачем. Достаточно просто писать отрываемый от системного АПИ код, и сажать его на моки.
Только где их взять. А писать их — время, и риск допустить ошибку.
А мне оно не очень надо самому, однако хотел бы принести пользу другим — таким, как разработчики Касперского или Аваста, или еще где драйверы применяются.
Есть желание разрабатывать такую песочницу (хотя, есть и план Б — система на основе старой доброй виртуалки).
Re[5]: Кто как пишет юнит-тесты для драйверов NT + Моя идея
Здравствуйте, LimyKurn, Вы писали:
LK>А можно взглянуть на тесты? Точнее, конкретно на "прокси" для функций.
Так они есть выше (см. mock-функции KeAcquireSpinLock/KeReleaseSpinLock).
LK>Если не хочешь продавать, то, может быть, попрошу тебя собрать эти прокси воедино и продокументировать бесплатно
Не понимаю, какая может быть польза от исходников, предназначенных для тестирования конкретных компонентов конкретного драйвера?
(не говоря уже о том, что исходники эти принадлежат компании, в которой я работаю, и выкладывать их в открытый доступ нельзя)
LK>Отсюда возникла идея перейти на новый WinDDK (полагаю, там C++ вплоть до 17), ну а VS для тестов и самих драйверов тоже — это само собой. LK>До какой версии можно WinDDK, если драйвер должен быть совместим с WinXP?
WDK 7.1 — последняя версия, где можно собирать под WinXP.
Под другими тоже можно, говорят, но это undocumented и работу такого драйвера на XP никто не гарантирует.
Re[6]: Кто как пишет юнит-тесты для драйверов NT + Моя идея
Здравствуйте, okman, Вы писали:
O>Некоторые сценарии, например отказ записи на диск при нехватке на нем места, O>вообще можно тестировать только так. Не будешь же на самом деле ради одного мелкого теста забивать весь O>диск мусором, чтобы проверить.
для этого есть виртуалки, где можно добавить диск на 50мб и легко прогнать на нем тест. Более того, это все скриптуется. Ваш к.о.
O>Представь, что нужно покрыть тестами код minifilter калбэка, который, скажем, блокирует изменение O>атрибутов безопасности файла или папки. Чтобы проверить это "в бою", нужно запустить виртуальную машину, O>перевести ее в тестовый режим, проинсталлировать туда свой драйвер, приаттачить его к нужному тому, O>затем с помощью тестового приложения, которое, кстати, еще придется написать, вызвать соответствующую O>функцию, обработать результат и каким-то образом сообщить "наверх" о том, пройден ли тест или провален. O>Да, а еще предусмотреть зависание виртуалки или выпадание ее в BSOD... Жуть.
не поверишь, но именно так это и делается. Естественно в атоматическом режиме.
Re[2]: Кто как пишет юнит-тесты для драйверов NT + Моя идея песочни
Здравствуйте, LimyKurn, Вы писали:
LK>Первое, что я сделал, это реализовал ExAllocatePoolWithTag на malloc, ExFreePoolWithTag на free, и макрос для проверки на утечки.
для этого есть driver verifier, опция pool tracking
Re[4]: Кто как пишет юнит-тесты для драйверов NT + Моя идея
Здравствуйте, mike_rs, Вы писали:
_>Здравствуйте, LimyKurn, Вы писали:
LK>>Первое, что я сделал, это реализовал ExAllocatePoolWithTag на malloc, ExFreePoolWithTag на free, и макрос для проверки на утечки. _>для этого есть driver verifier, опция pool tracking
Со 100% точностью скажу, что новичкам каждая минута дорогА и надо без перезапусков. А так как в небольшой мере мы всегда остаемся новичками — то и вот...
Ну, если только драйвера — это не та область (не считая отдельных упоротых проектов), где никто, никогда и никуда в принципе не торопится.
Re[3]: Кто как пишет юнит-тесты для драйверов NT + Моя идея песочни
Здравствуйте, mike_rs, Вы писали:
_>для этого есть виртуалки, где можно добавить диск на 50мб и легко прогнать на нем тест. Более того, это все скриптуется. Ваш к.о.
На мой скромный взгляд, это уход от идеологии юнит-тестирования куда-то далеко вбок.
Если таких тестов будут сотни и тысячи, ты замучаешься дергать эти виртуалки.
И занимать такие тесты будут уйму времени и других ресурсов.
То, что должно занимать секунды, будет занимать часы.
_>это фуфло, а не тестирование спинлока. irql не учтен, тесты не полные и не отражающие реального поведения системы.
С удовольствием рассмотрю другие варианты тестирования спинлока.
Re[4]: Кто как пишет юнит-тесты для драйверов NT + Моя идея песочни
Здравствуйте, okman, Вы писали:
O>То, что должно занимать секунды, будет занимать часы.
Бывает такое, что и часы не критичны людям. У нас был проект, где один из тестов выполнялся 15 часов. Конечно, не постоянно мы его дергали, и дергали только на некоторых этапах, после чего месяцами не трогали. Но все-таки.
Пока не вижу, чтобы кому-то была нужна нормально реализованная песочница. Если никому не будет нужна, то я не буду ее реализовывать.
На виртуалках — а опять же, раз никто никуда не торопится, то что им мешает сделать свое собственное решение, зачем нужно готовое? Не нужно.
Re[4]: Кто как пишет юнит-тесты для драйверов NT + Моя идея песочни
Здравствуйте, okman, Вы писали:
O>То, что должно занимать секунды, будет занимать часы.
одно другому не помеха. Но покрыть код моками и надеятся что в реальной жизни он не выстрелит — это неправильно
O>С удовольствием рассмотрю другие варианты тестирования спинлока.
этот тест конечно гарантирует, что спинлок отпущен после выполнения некоторого блока кода. Но это только часть проблемы. Сколько раз уже ловились баги, когда под захваченным спинлоком вызывается paged код, который выгрузился например, или функция ядра, которая не может быть вызвана на высоком irql, то-же сравнение строк без учета регистра. Да, скорее всего все кодовые страницы будут в этот момент уже загружены, но вот найдется такой сценарий, когда нет? и все зависнет.
А для корректного подсчета ссылок можно прямо в боевом коде обернуть Acq/Rel спинлока в функции-врапперы, которые будут крутить счетчик и при выгрузке драйвера (или в другой ключевой точке) проверять что этот счетчик нулевой, иначе генерировать багчек. Но тут мы опять приходим к варианту тестов в реальном окружении.
Re[5]: Кто как пишет юнит-тесты для драйверов NT + Моя идея песочни
Здравствуйте, mike_rs, Вы писали:
_>этот тест конечно гарантирует, что спинлок отпущен после выполнения некоторого блока кода. Но это только часть проблемы. Сколько раз уже ловились баги, когда под захваченным спинлоком вызывается paged код, который выгрузился например, или функция ядра, которая не может быть вызвана на высоком irql, то-же сравнение строк без учета регистра. Да, скорее всего все кодовые страницы будут в этот момент уже загружены, но вот найдется такой сценарий, когда нет? и все зависнет. _>А для корректного подсчета ссылок можно прямо в боевом коде обернуть Acq/Rel спинлока в функции-врапперы, которые будут крутить счетчик и при выгрузке драйвера (или в другой ключевой точке) проверять что этот счетчик нулевой, иначе генерировать багчек. Но тут мы опять приходим к варианту тестов в реальном окружении.
То, что ты описываешь — это по сути ближе к функциональным и интеграционным тестам.
Да, такие тесты должны выполняться в реальных условиях или таких условиях, которые к ним максимально приближены.
А задача юнит-тестов — проверить, что код удовлетворяет определенным требованиям (контрактам): подали X на вход —
получили Y на выходе. С моками это делать эффективнее, потому что, во-первых, тесты абстрагируются от среды, которая
здесь не нужна и только мешает, а во-вторых, можно проверить абсолютно любой сценарий, например, исполнение какой-то
редкой ветки кода в результате отказа системных API (такие вещи, по моему наблюдению, вообще никто не тестирует).
_>одно другому не помеха. Но покрыть код моками и надеятся что в реальной жизни он не выстрелит — это неправильно
А никто и не надеется. Впадать в крайности — вообще неправильно