LCOV/GCOV охват веток С++ по всему региону
мы используем LCOV/GCOV для обеспечения тестового покрытия наших проектов. В последнее время мы также попытались включить покрытие веток. Но похоже, что это просто не дает результатов, которые мы ожидали от представления разработчика на высоком уровне.
Использование покрытия ветвей с С++ удаляет отчет с помощью ветвей повсюду. Мы подозреваем (как указывает поиск проблем), что в основном код обработки исключений создает эти "скрытые ветки". И GCOV/LCOV, похоже, не пропускают их.
Я создал небольшой тестовый проект, чтобы показать проблему: https://github.com/ghandmann/lcov-branch-coverage-weirdness
В настоящее время мы используем Ubuntu 16.04. с:
- gcc v5.4
- lcov и genhtml v1.12
Наш производственный код построен с включенным С++ 11. Минимальный пример не строится с включенным С++ 11, но, когда мы немного экспериментировали со всеми различными опциями (стандарт С++, оптимизация, -fno-exceptions), мы не принесли пропущенного результата.
У кого-нибудь есть идеи? Tipps? Используем ли мы что-то не так? Это - как указано где-то еще - действительно ожидаемое поведение?
Спасибо четыре раза ваше время!
Update:
Как также указано в списке рассылки gcc-help эти "скрытые ветки" происходят из-за обработки исключений. Таким образом, добавление переключателя "-fno-exceptions" в gcc обеспечивает 100% -ный охват веток для "простых" программ. Но когда исключения отключены, gcc отказывается компилировать код, который фактически использует исключения (например, try-catch, throw). Поэтому для реального кода производства это не вариант. Похоже, вам нужно просто объявить, что 50% -ное покрытие будет новым 100% в этом случае.;)
Ответы
Ответ 1
Дело в том, что GCC также записывает информацию о ветвях для каждой строки, в которой возможен выход области из-за какого-то исключенного исключения (например, в Fedora 25 с GCC 6.3.1 и lcov 1.12).
Значение этой информации ограничено. Основным прецедентом для данных об охвате веток являются сложные операторы if, которые имеют многоклассовое логическое выражение, подобное этому:
if (foo < 1 && (bar > x || y == 0))
Скажите, что вам интересно проверить, распространяется ли ваш набор тестов на случай bar > x
, или если у вас есть только тестовые примеры, где y == 0
.
Для этого полезно использовать сбор данных о филиале и визуализацию с помощью lcov genhtml. Для простых if-утверждений типа
if (p == nullptr) {
return false;
}
return true;
вам не нужны данные о охвате веток, потому что вы видите, была ли ветка занята или нет, посмотрев на охват следующих строк.
Ввод genhtml
, который генерируется lcov
, находится в относительно простом текстовом формате (см. geninfo(1)
). Таким образом, вы можете выполнить его пост-обработку таким образом, чтобы все строки, начинающиеся с BRDA:
и не принадлежащие к if-statement, удаляются. См. Например filterbr.py
, который реализует этот подход. См. Также gen-coverage.py
для других шагов обработки lcov/genhtml и example где результирующий файл трассировки загружается в кодеки (кодеки не используют genhtml
, но могут импортировать файлы трассировки lcov и отображать данные о покрытии ветвей).
(не) Альтернативы
- Отключение исключений - это только вариант, когда ваш код на С++ не использует
- компиляция с чем-то вроде
-O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls
несколько уменьшает количество записанных данных покрытия ветвей, но не намного
- Clang поддерживает коллекцию покрытия стиля GCOV, но также реализует другой подход, называемый "исходный код покрытия" (скомпилируйте с
-fprofile-instr-generate -fcoverage-mapping
и пост-процесс с llvm-profdata
и llvm-cov
). Эта программная цепочка не поддерживает данные по охвату веток, хотя (по состоянию на 2017-05-01).
- по умолчанию lcov + genhtml не генерирует данные о покрытии ветки - иногда вам это действительно не нужно (см. выше), таким образом, это значит отключить его (см.
--rc lcov_branch_coverage=0
и --no-branch-coverage
)
Ответ 2
GCC добавит кучу элементов обработки исключений. Особенно, когда вы выполняете функциональные вызовы.
Вы можете исправить это, добавив -fno-exceptions -fno-inline
к вашей сборке.
Я должен добавить, вы, вероятно, хотите только эти флаги для тестирования.
Так что-то вроде этого:
g++ -O0 --coverage -fno-exceptions -fno-inline main.cpp -o test-coverage
Ответ 3
Вы можете попробовать g++ -O3 --coverage main.cpp -o testcov
. Я пробовал это с g++ - 5.4 в вашем файле, и он отлично работает, что означает, что исключения отбрасываются стандартными вызовами printf и string.
Фактически любой флаг оптимизации, отличный от O0
, заставит gcov игнорировать исключения, созданные для обычных вызовов библиотеки в файле CPP. Я не уверен, что нормальные исключения также будут оптимизированы (я так не думаю, но еще не пробовал).
Но я не уверен, что у вас есть какие-либо требования в вашем проекте, чтобы использовать только код O0 с вашим кодом, а не O1
, O2
, O3
или даже Os
.
Ответ 4
Я столкнулся с одной и той же проблемой, и я хочу избавиться от этих непокрытых ветвей из-за исключений. Я нашел подходящее решение для меня:
Я просто избегаю использования "throw exception" в своем коде, который я хочу использовать, напрямую. Я разработал класс, который предлагает некоторые методы, которые вместо этого исключают исключения. Поскольку класс исключений не является таким сложным, я действительно не забочусь о покрытии, поэтому я просто исключаю все с помощью LCOV_EXCL_START и LCOV_EXCL_STOP. В качестве альтернативы я мог бы также отключить покрытие ветки только для этого класса исключений.
Я признаю, что это не простое решение, но для моих целей оно идеально
по другим причинам тоже (мне нужно, чтобы класс исключений был гибким, чтобы я мог предлагать ему другую реализацию: однажды выбросив исключение, в другое время сделав что-то еще).