10.3 Использование gcov с оптимизацией GCC
Если вы планируете использовать gcov для оптимизации кода, необходимо сначала скомпилировать программу с помощью специального параметра GCC «--coverage». Помимо этого, можно использовать любые другие параметры GCC; но если вы хотите убедиться, что каждая строка вашей программы была выполнена, не следует компилировать с оптимизацией. На некоторых машинах оптимизатор может устранить некоторые простые строки кода, объединив их с другими строками. Например, код такого вида:
if (a != b) c = 1; else c = 0;
может быть скомпилирован в одну инструкцию на некоторых машинах. В этом случае gcov не может рассчитать отдельные счетчики выполнения для каждой строки, так как нет отдельного кода для каждой строки. В результате gcov вывод выглядит так, если программа была скомпилирована с оптимизацией:
100: 12:if (a != b) 100: 13: c = 1; 100: 14:else 100: 15: c = 0;
Вывод показывает, что этот блок кода, объединенный оптимизацией, выполнился 100 раз. В некотором смысле этот результат верен, поскольку была только одна инструкция, представляющая все четыре строки. Однако вывод не указывает, сколько раз результат был 0, и сколько раз результат был 1.
Встраиваемые функции могут создавать неожиданные значения счетчиков строк. Счетчики строк отображаются для исходного кода встраиваемой функции, но то, что отображается, зависит от места встраивания функции или от того, встроена ли она вообще.
Если функция не встроена, компилятор должен выдать внестрочный экземпляр функции в любом объектном файле, которому она нужна. Если fileA.o и fileB.o оба содержат внестрочные тела определенной встраиваемой функции, они также оба будут содержать счетчики покрытия для этой функции. При связывании fileA.o и fileB.o компоновщик на многих системах выберет одно из этих внестрочных тел для всех вызовов этой функции и удалит или проигнорирует другое. К сожалению, он не удалит счетчики покрытия для неиспользуемого тела функции. Следовательно, при инструментировании все, кроме одного использования этой функции, будут показывать нулевые значения.
Если функция встроена в нескольких местах, структура блоков в каждом месте может отличаться. Например, условие может теперь вычисляться на этапе компиляции в некоторых случаях. Поскольку покрытие всех обращений к встроенной функции будет показано для одних и тех же исходных строк, сами счетчики строк могут показаться несогласованными.
Длительно работающие приложения могут использовать __gcov_reset и __gcov_dump средства для ограничения сбора профиля областью программы, которая нас интересует. Вызов __gcov_reset(void) обнулит все счетчики профиля во время выполнения, а вызов __gcov_dump(void) приведет к выводу профильной информации, собранной в этот момент, в файлы вывода .gcda. Инструментированные приложения используют статический деструктор с приоритетом 99 для вызова функции __gcov_dump. Таким образом, __gcov_dump выполняется после всех пользовательских статических деструкторов, а также обработчиков, зарегистрированных с atexit.
Если исполняемый файл загружает динамическую общую библиотеку с помощью функции dlopen, требуется -Wl,--dynamic-list-data для вывода всех профильных данных.
Отчеты профилирующей библиотеки во время выполнения сообщают различные ошибки, связанные с обработкой профиля и сохранением профиля. Ошибки выводятся в стандартный поток ошибок или в файл ‘GCOV_ERROR_FILE’, если используется переменная среды. Для немедленного завершения после возникновения ошибки установите переменную среды ‘GCOV_EXIT_AT_ERROR’. Это может помочь пользователям найти конфликты профилей, которые приводят к вводящим в заблуждение профилям.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-13.3.0/gcc/Gcov-and-Optimization.html