Покрытие кода OpenBSD
Как-то я написал заметку про обеспечение качества в проекте OpenBSD и написал, что в дереве исходного кода проекта есть регресионные тесты. Судя по статистике коммитов в 2014 году набор тестов увеличивается, но сами тесты запускались нерегулярно, а время от времени и из-за этого часть из них была сломана. В конце прошлого кода один из разработчиков взял себя в руки и настроил ежедневный запуск тестов на последнем снапшоте OpenBSD:
On 15:35 Thu 08 Dec , Alexander Bluhm wrote:
> Hi,
>
> We are doing some regression and performance testing with OpenBSD
> -currrent. The daily results are published on this web site.
>
> http://bluhm.genua.de/
>
> bluhm
И сразу стало понятно состояние тестов - только 72% тестов из них были рабочими. Постепенно это количество Александр увеличил до 91%.
Мне стало интересно узнать какой процент кода OpenBSD покрывается этими тестами. До сих пор никто этого не делал (а если и делал, то не публиковал результаты).
Как известно, некоторые компоненты проекта популярны гораздо больше, чем сама ОС и используются отдельно от неё. Например это OpenSSH, LibreSSL и некоторые другие компоненты. По видимому вследствие популярности первых двух компонентов покрытие для них регулярно измеряет компания Froglogic - http://www.opencoverage.net/ и выкладывает результаты в публичный доступ, так в OpenSSH покрыто 30%, а в LibreSSL 27% кода. А вот для всех остальных компонентов процент покрытия неизвестен. Поэтому я решил это измерить его сам.
Вообще измерение покрытия для приложений, написанных на компилируемых языках, происходит так:
- нужно инструментировать код приложения с помощью компилятора
- скомпилировать приложение
- запустить тесты для этого приложения
- собрать результаты и сделать отчёт
В моём случае все оказалось чуть сложнее. Во-первых из-за системного вызова pledge(), во-вторых из-за отсутствия нужных функций в ядре. Дальше будут детали. Если вы хотите посмотреть на результаты измерений без деталей, то листайте в самый низ.
Коварный pledge()
В версии 5.9 добавили системный вызов pledge.2. Эта функция и одноименный системный вызов нужны для контроля разрешённых классов системных вызовов в модифицированном приложении. Если приложение нарушает правила и пытается вызвать системный вызов, не описанный в аннотации, то ядро принудительно завершит приложение сигналом SIGABRT. То есть для измерения покрытия приложению нужны системные вызовы, которые вовсе не обязательно описаны в коде этого приложения. Понять это получилось не сразу и тестирование приложений с поддержкой gcov сопровождались такими сообщениями:
Testing suffix "/usr/bin/gcc" "xx"
===> bc
rm -f *.log t19
t1
Abort trap (core dumped)
*** Error 134 in target 'regress' (ignored)
Abort trap (core dumped)
*** Error 134 in bc (<bsd.regress.mk>:106 'regress': @echo usr.bin/bc/t1 | tee -a /dev/null /dev/null 2>&1 > /dev/null)
*** Error 1 in /usr/src/regress/usr.bin (<bsd.subdir.mk>:48 'all')
# dmesg | tail -2
tee(5031): syscall 5 "rpath"
tee(79812): syscall 5 "rpath"
Abort trap (core dumped)
#
Решил проблему просто - сделал скрипт, который проверяет классы системных вызовов в каждом приложении и добавляет “wpath”, “cpath” и “rpath”, если они отсутствуют.
Покрытие кода ядра
С покрытием кода, работающего в пространстве пользователя, всё понятно. С ядром ситуация немного сложнее. Чтобы во время работы ядро генерировало информацию о покрытии нужно:
- добавить в ядро дополнительные функции для сбора статистики об использовании функций ядра
- изменить ldscript для ядра
- написать утилиту, которая будет из памяти извлекать данные о покрытии
В 2004 году для FreeBSD эту работу уже делали, но насколько это сейчас работоспособно я не знаю.
Кстати ни одна ОС семейства BSD не измеряет покрытие кода. Интересно с чем это связано? Если доведу работу до конца, то буду первопроходцем :)
Результаты
Стандарт де-факто для создания отчётов это утилиты gcov и lcov проекта Linux Testing Project. Для меня удобнее gcovr, поэтому я сделал для утилиты порт, чтобы проще было устанавливать.
Чтобы сделать HTML отчёт нужно запустить её с такими опциями:
gcovr --html --html-details --output=coverage.html --verbose --keep --print-summary --root `pwd` .
В результате у меня получился отчёт для всего кода, за исключением нескольких директорий: lib, libexec, gnu, regress, games, sys. Общий результат покрытия составил 10% - https://ligurio.github.io/openbsd-tests/6.0/coverage.html