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’. Это может помочь пользователям обнаружить конфликты в профилях, которые приводят к недостоверной информации о профилях.
Далее: Файлы данных gcov, Предыдущий: Вызов Gcov, Вверх: Gcov [Оглавление][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Gcov-and-Optimization.html