11.2 Запуск gcov
gcov [options] files
gcov принимает следующие параметры:
-a--all-blocks-
Записывать отдельные счётчики выполнения для каждого базового блока. Обычно gcov выводит счётчики выполнения только для основных блоков строки. С помощью этого параметра можно определить, выполняются ли блоки внутри одной строки.
-b--branch-probabilities-
Записывать частоты ветвлений в выходной файл, а сводную информацию о ветвлениях — в стандартный вывод. Этот параметр позволяет увидеть, как часто выполнялось каждое ветвление программы. Безусловные ветвления не отображаются, если не указан параметр -u.
-c--branch-counts-
Записывать частоты ветвлений в виде числа выполнений ветвей, а не в виде процента выполненных ветвей.
-g--conditions-
Записывать покрытие условий в выходной файл, а сводную информацию об условиях — в стандартный вывод. Этот параметр позволяет увидеть, оказывали ли условия в программе хотя бы один раз независимое влияние на результат булева выражения (покрытие модифицированных условий и решений). Для этого необходимо скомпилировать исходный код с параметром -fcondition-coverage.
-e--prime-paths-
Записывать покрытие путей в выходной файл, а сводную информацию о путях — в стандартный вывод. Этот параметр позволяет увидеть, сколько простых путей максимальной длины было выполнено хотя бы один раз. Путь — это последовательность базовых блоков. Путь является простым, если в нём нет повторяющихся блоков (нет циклов), кроме, возможно, первого и последнего блока; он является максимальным, если это простой путь максимальной длины. В обычном выводе этот параметр показывает только количество покрытых путей. Для получения более подробной информации о путях можно использовать --prime-paths-lines или --prime-paths-source. При использовании --json-format в вывод включаются все сведения о путях. Для этого необходимо скомпилировать исходный код с параметром -fpath-coverage.
--prime-paths-lines [=type]-
Записывать покрытие путей в выходной файл, а сводную информацию о путях — в стандартный вывод. Этот параметр позволяет увидеть, сколько простых путей максимальной длины было выполнено хотя бы один раз, а также получить подробный отчёт о покрытых или непокрытых путях и о том, как их покрыть. Этот режим полезен для автоматизированной отчётности и отслеживания прогресса. type можно опустить или указать одно из следующих значений:
- uncovered — включить непокрытые (невыполненные) пути. Это значение используется по умолчанию.
- covered — включить покрытые (выполненные) пути.
- both — включить все пути. Это эквивалентно одновременному использованию covered и uncovered.
Пример вывода параметра --prime-paths-lines:
paths covered 12 of 15 path 2 not covered: lines 8 8(false) 11(true) 11 13(true) 13(true) 14 17 path 3 not covered: lines 8 8(false) 11(true) 11 13(true) 13(false) 16 17 path 4 not covered: lines 8 8(false) 11(true) 11 13(false) 16 17
Это означает, что для покрытия пути 2 необходимо выполнить строки 8, 11, 13, 14 и 17, вычислив условие в строке 8 как ложное, а условия в строках 11 и 13 — как
false. --prime-paths-source [=type]-
Записывать покрытие путей в выходной файл, а сводную информацию о путях — в стандартный вывод. Этот параметр позволяет увидеть, сколько простых путей максимальной длины было выполнено хотя бы один раз, а также получить подробный отчёт о непокрытых путях и о том, как их покрыть. Этот режим полезен для изучения путей и взаимодействий между частями программы. type можно опустить или указать одно из следующих значений:
- uncovered — включить непокрытые (невыполненные) пути. Это значение используется по умолчанию.
- covered — включить покрытые (выполненные) пути.
- both — включить все пути. Это эквивалентно одновременному использованию covered и uncovered.
Пример вывода параметра --prime-paths-source:
path 10 not covered: BB 3: 8: for (i = 0; i < 10; i++) BB 3: 9: total += i; BB 4: (false) 8: for (i = 0; i < 10; i++) BB 5: (true) 11: int v = total > 100 ? 1 : 2; BB 6: 11: int v = total > 100 ? 1 : 2; BB 8: (false) 13: if (total != 45 && v == 1) BB 11: 16: printf ("Success\n"); BB 12: 17: return 0;В первом столбце (BB) указана последовательность базовых блоков (см. -w). В среднем столбце (true/false) указано значение условия для этой строки. В третьем столбце указан номер строки. В четвёртом столбце приведена сама строка. Чтобы покрыть путь 10, эти строки необходимо выполнить в указанном порядке.
-d--display-progress-
Отображать ход выполнения в стандартном выводе.
-f--function-summaries-
Выводить сводную информацию для каждой функции в дополнение к сводной информации на уровне файла.
--include regex-
Включать функции, соответствующие regex. Этот параметр заставляет
gcovвыводить сведения только о функциях, соответствующих расширенному регулярному выражению regex. Этот флаг можно комбинировать с --exclude. Если функция соответствует и включающему, и исключающему фильтру, применяется последний из них. По умолчаниюgcovвыводит сведения обо всех функциях, но при использовании--includeбудут показаны только функции, соответствующие включающему фильтру. --exclude regex-
Исключать функции, соответствующие regex. Этот параметр заставляет
gcovне выводить сведения о функциях, соответствующих расширенному регулярному выражению regex. Этот флаг можно комбинировать с --include. Если функция соответствует и включающему, и исключающему фильтру, применяется последний из них. По умолчаниюgcovвыводит сведения обо всех функциях, а при использовании --exclude соответствующие ему функции исключаются. -h--help-
Вывести справку по использованию
gcov(в стандартный вывод) и завершить работу без дальнейшей обработки. -j--json-format-
Выводить файл gcov в удобном для разбора промежуточном формате JSON, для создания которого не требуется исходный код. Файл JSON сжимается алгоритмом gzip и имеет расширение .gcov.json.gz.
Структура JSON имеет следующий вид:
{ "current_working_directory": "foo/bar", "data_file": "a.out", "format_version": "2", "gcc_version": "11.1.1 20210510" "files": ["$file"] }Поля корневого элемента имеют следующий смысл:
- current_working_directory: рабочий каталог, в котором была скомпилирована единица компиляции
- data_file: имя файла данных (GCDA)
-
format_version: семантическая версия формата
Изменения в версии 2:
- calls: добавлена информация о вызовах функций
- gcc_version: версия компилятора GCC
Каждый элемент file имеет следующий вид:
{ "file": "a.c", "functions": ["$function"], "lines": ["$line"] }Поля элемента file имеют следующий смысл:
- file_name: имя исходного файла
Каждый элемент function имеет следующий вид:
{ "blocks": 2, "blocks_executed": 2, "demangled_name": "foo", "end_column": 1, "end_line": 4, "execution_count": 1, "name": "foo", "start_column": 5, "start_line": 1 }Поля элемента function имеют следующий смысл:
- blocks: количество блоков в функции
- blocks_executed: количество выполненных блоков функции
- demangled_name: деманглированное имя функции
- end_column: столбец исходного файла, в котором заканчивается функция
- end_line: строка исходного файла, в которой заканчивается функция
- execution_count: количество выполнений функции
- name: имя функции
- start_column: столбец исходного файла, в котором начинается функция
- start_line: строка исходного файла, в которой начинается функция
Обратите внимание, что нумерация строк и столбцов начинается с 1. В текущей реализации start_line и start_column не учитывают параметры шаблона и начальный тип возвращаемого значения, однако, вероятно, в будущем это будет исправлено.
Каждый элемент line имеет следующий вид:
{ "block_ids": ["$block_id"], "branches": ["$branch"], "calls": ["$call"], "count": 2, "conditions": ["$condition"], "line_number": 15, "unexecuted_block": false, "function_name": "foo", }Ветви и вызовы присутствуют только при использовании параметра -b. Поля элемента line имеют следующий смысл:
- block_ids: идентификаторы базовых блоков, относящихся к строке
- count: количество выполнений строки
- line_number: номер строки
- unexecuted_block: флаг, указывающий, содержит ли строка невыполненный блок (не все операторы в строке выполнены)
- function_name: имя функции, которой принадлежит этот элемент line (может отсутствовать для строки со встроенными операторами)
Каждый элемент branch имеет следующий вид:
{ "count": 11, "destination_block_id": 17, "fallthrough": true, "source_block_id": 13, "throw": false }Поля элемента branch имеют следующий смысл:
- count: количество выполнений ветви
- fallthrough: значение true, если ветвь является переходом по умолчанию
- throw: значение true, если ветвь является ветвью исключения
- isource_block_id: идентификатор базового блока, в котором происходит эта ветвь
- destination_block_id: идентификатор базового блока, в который переходит эта ветвь
Каждый элемент call имеет следующий вид:
{ "destination_block_id": 1, "returned": 11, "source_block_id": 13 }Поля элемента call имеют следующий смысл:
- returned: количество возвратов из вызова функции (число вызовов равно line::count)
- isource_block_id: идентификатор базового блока, в котором происходит этот вызов
- destination_block_id: идентификатор базового блока, в который продолжается выполнение после возврата из этого вызова
Каждый элемент condition имеет следующий вид:
{ "count": 4, "covered": 2, "not_covered_false": [], "not_covered_true": [0, 1], }Поля элемента condition имеют следующий смысл:
- count: количество результатов вычисления условия в этом выражении
- covered: количество покрытых результатов вычисления условия в этом выражении
- not_covered_true: индексы термов, которые в этом выражении не принимали значение true
- not_covered_false: индексы термов, которые в этом выражении не принимали значение false
-H--human-readable-
Выводить счётчики в удобном для чтения формате (например, 24.6k).
-k--use-colors-
Использовать цвета для строк кода с нулевым покрытием. Для обычных строк используется красный цвет, для строк с исключениями — голубой. Те же цвета используются для базовых блоков с параметром -a.
-l--long-file-names-
Создавать длинные имена файлов для включённых исходных файлов. Например, если заголовочный файл x.h содержит код и включён в файл a.c, то при запуске
gcovдля файла a.c будет создан выходной файл с именем a.c##x.h.gcov вместо x.h.gcov. Это может быть полезно, если x.h включён в несколько исходных файлов и необходимо увидеть отдельный вклад каждого из них. При использовании параметра ‘-p’ имена обоих файлов — включающего и включённого — будут полными путями. -m--demangled-names-
Выводить в результатах деманглированные имена функций. По умолчанию отображаются манглированные имена функций.
-M--filter-on-demangled-
Сопоставлять параметры --include и --exclude с деманглированными именами. Это влияет только на сопоставление и не подразумевает использование --demangled-names, однако этот параметр можно безопасно комбинировать с ним.
-n--no-output-
Не создавать выходной файл
gcov. -o directory|file--object-directory directory--object-file file-
Указать каталог с файлами данных gcov или путь к объектному файлу. Для поиска файлов данных .gcno и .gcda используется этот параметр. Если указан каталог, файлы данных находятся в нём и имеют имена входных файлов без расширений. Если здесь указан файл, файлы данных имеют имя этого файла без расширения.
-p--preserve-paths-
Сохранять полную информацию о путях в именах создаваемых файлов .gcov. Без этого параметра используется только компонент имени файла. При использовании этого параметра включаются все каталоги, символы ‘/’ заменяются символами ‘#’, компоненты каталогов . удаляются, а неудаляемые компоненты .. переименовываются в ‘^’. Это полезно, если исходные файлы находятся в нескольких разных каталогах.
-q--use-hotness-colors-
Выводить цветные данные в стиле perf для часто выполняемых строк. Обозначения цветовой шкалы печатаются в самом начале выходного файла.
-r--relative-only-
Выводить сведения только об исходных файлах с относительным путём (после удаления префикса исходного пути). Абсолютные пути обычно соответствуют системным заголовочным файлам, а покрытие любых встроенных функций в них, как правило, не представляет интереса.
-s directory--source-prefix directory-
Префикс имён исходных файлов, который следует удалить при создании файлов с данными о покрытии. Этот параметр полезен при сборке в отдельном каталоге, когда путь к каталогу исходных файлов не требуется включать в имена выходных файлов. Обратите внимание, что проверка префикса выполняется до определения того, является ли исходный файл абсолютным.
-t--stdout-
Выводить данные в стандартный вывод, а не в выходные файлы.
-u--unconditional-branches-
Если выводятся вероятности ветвлений, включать вероятности безусловных ветвлений. Безусловные ветвления обычно не представляют интереса.
-v--version-
Вывести номер версии
gcov(в стандартный вывод) и завершить работу без дальнейшей обработки. -w--verbose-
Выводить подробную информацию о базовых блоках и дугах.
-x--hash-filenames-
При использовании –preserve-paths gcov использует полный путь к исходным файлам для создания имени выходного файла. Это может привести к созданию слишком длинных имён, превышающих ограничения файловой системы. Этот параметр создаёт имена вида source-file##md5.gcov, где компонент source-file — это последняя часть имени файла, а компонент md5 вычисляется по полному манглированному имени, которое было бы использовано в противном случае. Этот параметр является альтернативой –preserve-paths в системах с ограничениями файловой системы.
gcov следует запускать из того же каталога, который был текущим при вызове компилятора. В противном случае программа не сможет найти исходные файлы. gcov создаёт в текущем каталоге файлы с именами mangledname.gcov. В них содержатся данные о покрытии соответствующих исходных файлов. Для каждого исходного (или заголовочного) файла с кодом, скомпилированного для создания файлов данных, создаётся один файл .gcov. Часть mangledname в имени выходного файла обычно совпадает с именем исходного файла, но может быть сложнее, если указаны параметры ‘-l’ или ‘-p’. Подробнее см. описание этих параметров.
Если вызвать gcov с несколькими входными файлами, данные из каждого входного файла суммируются. Обычно его вызывают с тем же списком файлов, что и при окончательной компоновке исполняемого файла.
Файлы .gcov содержат поля, разделённые символом ‘:’, а также исходный код программы. Формат выглядит следующим образом:
execution_count:line_number:source line text
При соответствующем параметре командной строки после каждой строки могут следовать дополнительные сведения о блоках. Значение execution_count равно ‘-’ для строк, не содержащих кода. Невыполненные строки помечаются символами ‘#####’ или ‘=====’ в зависимости от того, достижимы ли они по обычным путям или только по путям с исключениями, например через обработчики исключений C++. При использовании параметра ‘-a’ невыполненные блоки помечаются символами ‘$$$$$’ или ‘%%%%%’ в зависимости от того, достижим ли базовый блок по обычным путям или по путям с исключениями. Выполненные базовые блоки, содержащие оператор с нулевым значением execution_count, заканчиваются символом ‘*’ и окрашиваются в пурпурный цвет при использовании параметра -k. Эта возможность не поддерживается в Ada.
Обратите внимание, что GCC может полностью удалить тела функций, которые не нужны, например если они встроены во всех местах вызова. Такие функции помечаются символом ‘-’, что может сбивать с толку. Используйте параметры -fkeep-inline-functions и -fkeep-static-functions, чтобы сохранить эти функции и позволить gcov правильно показывать их execution_count.
Некоторые строки данных в начале файла имеют значение line_number, равное нулю. Эти строки предварительной информации имеют следующий вид:
-:0:tag:value
По мере развития gcov порядок и количество этих строк предварительной информации будут меняться — не следует рассчитывать на то, что они останутся неизменными. Используйте tag, чтобы найти конкретную строку предварительной информации.
Дополнительные сведения о блоках имеют следующий вид:
tag information
Значение information удобно для чтения человеком, но в то же время устроено достаточно просто для машинного разбора.
При выводе процентов значения 0% и 100% указываются только тогда, когда фактические значения точно равны 0% и 100% соответственно. Другие значения, которые обычно округлялись бы до 0% или 100%, выводятся как ближайшее значение, не равное граничному.
При использовании gcov сначала необходимо скомпилировать программу со специальным параметром GCC ‘--coverage’. Он указывает компилятору создать дополнительные сведения, необходимые gcov (в частности, граф потока управления программы), и добавить в объектные файлы код для создания дополнительных данных профилирования, необходимых gcov. Эти дополнительные файлы помещаются в каталог с объектным файлом.
При запуске программы создаются выходные данные профилирования. Для каждого исходного файла, скомпилированного с параметром -fprofile-arcs, в каталоге объектного файла будет создан соответствующий файл .gcda.
Теперь запуск gcov с именами исходных файлов программы в качестве аргументов выведет список кода вместе с частотой выполнения каждой строки. Например, если программа называется tmp.cpp, при использовании базовых средств gcov вы увидите следующее:
$ g++ --coverage tmp.cpp -c $ g++ --coverage tmp.o $ a.out $ gcov tmp.cpp -m File 'tmp.cpp' Lines executed:92.86% of 14 Creating 'tmp.cpp.gcov'
Файл tmp.cpp.gcov содержит вывод gcov. Пример:
-: 0:Source:tmp.cpp
-: 0:Working directory:/home/gcc/testcase
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:template<class T>
-: 4:class Foo
-: 5:{
-: 6: public:
1*: 7: Foo(): b (1000) {}
------------------
Foo<char>::Foo():
#####: 7: Foo(): b (1000) {}
------------------
Foo<int>::Foo():
1: 7: Foo(): b (1000) {}
------------------
2*: 8: void inc () { b++; }
------------------
Foo<char>::inc():
#####: 8: void inc () { b++; }
------------------
Foo<int>::inc():
2: 8: void inc () { b++; }
------------------
-: 9:
-: 10: private:
-: 11: int b;
-: 12:};
-: 13:
-: 14:template class Foo<int>;
-: 15:template class Foo<char>;
-: 16:
-: 17:int
1: 18:main (void)
-: 19:{
-: 20: int i, total;
1: 21: Foo<int> counter;
-: 22:
1: 23: counter.inc();
1: 24: counter.inc();
1: 25: total = 0;
-: 26:
11: 27: for (i = 0; i < 10; i++)
10: 28: total += i;
-: 29:
1*: 30: int v = total > 100 ? 1 : 2;
-: 31:
1: 32: if (total != 45)
#####: 33: printf ("Failure\n");
-: 34: else
1: 35: printf ("Success\n");
1: 36: return 0;
-: 37:}
Обратите внимание, что строка 7 отображается в отчёте несколько раз. Первое вхождение показывает общее количество выполнений строки, а следующие два относятся к экземплярам конструкторов класса Foo. Кроме того, в строке 30 есть невыполненные базовые блоки, поэтому счётчик выполнений помечен звёздочкой.
При использовании параметра -a отображаются отдельные счётчики блоков, и вывод выглядит следующим образом:
-: 0:Source:tmp.cpp
-: 0:Working directory:/home/gcc/testcase
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:template<class T>
-: 4:class Foo
-: 5:{
-: 6: public:
1*: 7: Foo(): b (1000) {}
------------------
Foo<char>::Foo():
#####: 7: Foo(): b (1000) {}
------------------
Foo<int>::Foo():
1: 7: Foo(): b (1000) {}
------------------
2*: 8: void inc () { b++; }
------------------
Foo<char>::inc():
#####: 8: void inc () { b++; }
------------------
Foo<int>::inc():
2: 8: void inc () { b++; }
------------------
-: 9:
-: 10: private:
-: 11: int b;
-: 12:};
-: 13:
-: 14:template class Foo<int>;
-: 15:template class Foo<char>;
-: 16:
-: 17:int
1: 18:main (void)
-: 19:{
-: 20: int i, total;
1: 21: Foo<int> counter;
1: 21-block 0
-: 22:
1: 23: counter.inc();
1: 23-block 0
1: 24: counter.inc();
1: 24-block 0
1: 25: total = 0;
-: 26:
11: 27: for (i = 0; i < 10; i++)
1: 27-block 0
11: 27-block 1
10: 28: total += i;
10: 28-block 0
-: 29:
1*: 30: int v = total > 100 ? 1 : 2;
1: 30-block 0
%%%%%: 30-block 1
1: 30-block 2
-: 31:
1: 32: if (total != 45)
1: 32-block 0
#####: 33: printf ("Failure\n");
%%%%%: 33-block 0
-: 34: else
1: 35: printf ("Success\n");
1: 35-block 0
1: 36: return 0;
1: 36-block 0
-: 37:}
В этом режиме каждый базовый блок отображается только в одной строке — последней строке блока. Многострочный блок учитывается только в счётчике выполнений последней строки, а остальные строки не отмечаются как содержащие код, если только на них не заканчиваются предыдущие блоки. Отображается общий счётчик выполнений строки, а в следующих строках — счётчики выполнений отдельных блоков, заканчивающихся в этой строке. После каждого блока выводятся счётчики ветвлений и вызовов блока, если указан параметр -b.
Из-за особенностей инструментирования вызовов в GCC счётчик вызовов может отображаться после строки без отдельных блоков. Как видно, строка 33 содержит невыполненный базовый блок.
При использовании параметра -b вывод выглядит следующим образом:
-: 0:Source:tmp.cpp
-: 0:Working directory:/home/gcc/testcase
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:template<class T>
-: 4:class Foo
-: 5:{
-: 6: public:
1*: 7: Foo(): b (1000) {}
------------------
Foo<char>::Foo():
function Foo<char>::Foo() called 0 returned 0% blocks executed 0%
#####: 7: Foo(): b (1000) {}
------------------
Foo<int>::Foo():
function Foo<int>::Foo() called 1 returned 100% blocks executed 100%
1: 7: Foo(): b (1000) {}
------------------
2*: 8: void inc () { b++; }
------------------
Foo<char>::inc():
function Foo<char>::inc() called 0 returned 0% blocks executed 0%
#####: 8: void inc () { b++; }
------------------
Foo<int>::inc():
function Foo<int>::inc() called 2 returned 100% blocks executed 100%
2: 8: void inc () { b++; }
------------------
-: 9:
-: 10: private:
-: 11: int b;
-: 12:};
-: 13:
-: 14:template class Foo<int>;
-: 15:template class Foo<char>;
-: 16:
-: 17:int
function main called 1 returned 100% blocks executed 81%
1: 18:main (void)
-: 19:{
-: 20: int i, total;
1: 21: Foo<int> counter;
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
-: 22:
1: 23: counter.inc();
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
1: 24: counter.inc();
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
1: 25: total = 0;
-: 26:
11: 27: for (i = 0; i < 10; i++)
branch 0 taken 91% (fallthrough)
branch 1 taken 9%
10: 28: total += i;
-: 29:
1*: 30: int v = total > 100 ? 1 : 2;
branch 0 taken 0% (fallthrough)
branch 1 taken 100%
-: 31:
1: 32: if (total != 45)
branch 0 taken 0% (fallthrough)
branch 1 taken 100%
#####: 33: printf ("Failure\n");
call 0 never executed
branch 1 never executed
branch 2 never executed
-: 34: else
1: 35: printf ("Success\n");
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
1: 36: return 0;
-: 37:}
Для каждой функции выводится строка с количеством её вызовов, количеством возвратов и процентом выполненных блоков функции.
Для каждого базового блока после его последней строки выводится строка с описанием ветвления или вызова, которым заканчивается этот базовый блок. Если в одной строке исходного кода заканчиваются несколько базовых блоков, для неё может быть выведено несколько ветвлений и вызовов. В этом случае каждому ветвлению и вызову присваивается номер. Простого способа сопоставить эти ветвления и вызовы с конструкциями исходного кода нет. Однако в целом ветвление или вызов с наименьшим номером соответствует самой левой конструкции в строке исходного кода.
Если ветвление выполнялось хотя бы один раз, выводится процент, обозначающий отношение числа выполнений ветви к числу выполнений самого ветвления. В противном случае выводится сообщение «никогда не выполнялось».
Если вызов выполнялся хотя бы один раз, выводится процент, обозначающий отношение числа возвратов из вызова к числу выполнений самого вызова. Обычно это значение равно 100%, но оно может быть меньше для функций, вызывающих exit или longjmp, поэтому такие функции могут возвращать управление не при каждом вызове.
При использовании параметра -g вывод выглядит следующим образом:
$ gcov -t -m -g tmp
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:int
1: 4:main (void)
-: 5:{
-: 6: int i, total;
1: 7: total = 0;
-: 8:
11: 9: for (i = 0; i < 10; i++)
condition outcomes covered 2/2
10: 10: total += i;
-: 11:
1*: 12: int v = total > 100 ? 1 : 2;
condition outcomes covered 1/2
condition 0 not covered (true)
-: 13:
1*: 14: if (total != 45 && v == 1)
condition outcomes covered 1/4
condition 0 not covered (true)
condition 1 not covered (true false)
#####: 15: printf ("Failure\n");
-: 16: else
1: 17: printf ("Success\n");
1: 18: return 0;
-: 19:}
Для каждого условия выводится число выполненных результатов и общее количество результатов; если есть непокрытые результаты, для каждого условия выводится строка с непокрытым результатом в скобках. Условия обозначаются индексами: индекс 0 соответствует самому левому условию. В a || (b && c) a — это условие 0, b — условие 1, а c — условие 2.
Результат считается покрытым, если он оказывает независимое влияние на решение; это также называется маскирующим MC/DC (покрытием модифицированных условий и решений). В этом примере результат решения — true, и условие a вычисляется, но не считается покрытым. Причина в том, что a не может независимо повлиять на решение: для изменения решения должны поменяться значения и a, и b.
$ gcov -t -m -g tmp
-: 0:Source:tmp.c
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
-: 2:
1: 3:int main()
-: 4:{
1: 5: int a = 1;
1: 6: int b = 0;
-: 7:
1: 8: if (a && b)
condition outcomes covered 1/4
condition 0 not covered (true false)
condition 1 not covered (true)
#####: 9: printf ("Success!\n");
-: 10: else
1: 11: printf ("Failure!\n");
-: 12:}
При компиляции с параметрами --coverage -fpath-coverage и использовании параметра -e вывод будет выглядеть следующим образом:
$ gcov -t -e tmp
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
-: 2:
paths covered 4 of 15
1: 3:int main ()
-: 4:{
-: 5: int i, total;
1: 6: total = 0;
-: 7:
11: 8: for (i = 0; i < 10; i++)
10: 9: total += i;
-: 10:
1*: 11: int v = total > 100 ? 1 : 2;
-: 12:
1*: 13: if (total != 45 && v == 1)
#####: 14: printf ("Failure\n");
-: 15: else
1: 16: printf ("Success\n");
1: 17: return 0;
-: 18:}
Этот вывод полезен, чтобы приблизительно определить, где отсутствует покрытие, и проверить, как различные входные данные меняют покрытие. Параметр --prime-paths-source полезен для изучения путей.
$ gcov -t --prime-paths-source tmp
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
-: 2:
paths covered 4 of 15
path 1:
BB 2: 3:int main ()
BB 2: 6: total = 0;
BB 2: 8: for (i = 0; i < 10; i++)
BB 4: (false) 8: for (i = 0; i < 10; i++)
BB 5: (true) 11: int v = total > 100 ? 1 : 2;
BB 6: 11: int v = total > 100 ? 1 : 2;
BB 8: (true) 13: if (total != 45 && v == 1)
BB 9: (true) 13: if (total != 45 && v == 1)
BB 10: 14: printf ("Failure\n");
BB 12: 17: return 0;
В этом режиме gcov выводит подробные сведения о непокрытых путях. В первом столбце перечислена последовательность базовых блоков (BB). Во втором столбце указано, какое решение следует принять в этой строке, если оно есть. В последних столбцах указаны номер строки и сама строка. Это полезно для изучения путей, в частности тех, которые трудно покрыть или которые вообще недостижимы. Строки могут повторяться, например, в цикле for, если одна и та же строка входит в несколько базовых блоков. Этот режим предназначен для людей и помогает понять, какой код выполняется во время тестирования или при заданных входных данных. Вывод получается довольно подробным; чтобы сосредоточиться на отдельных функциях, его можно использовать вместе с фильтрами --include и --exclude.
Более компактный вывод можно получить с помощью --prime-paths-lines. Он выглядит следующим образом:
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
-: 2:
paths covered 4 of 15
path 1 not covered: lines 8 8(false) 11(true) 11 13(true) 13(true) 14 17
path 2 not covered: lines 8 8(false) 11(true) 11 13(true) 13(false) 16 17
path 3 not covered: lines 8 8(false) 11(true) 11 13(false) 16 17
path 4 not covered: lines 8 8(false) 11(false) 11 13(true) 13(true) 14 17
path 5 not covered: lines 8 8(false) 11(false) 11 13(true) 13(false) 16 17
path 6 not covered: lines 8 8(false) 11(false) 11 13(false) 16 17
path 8 not covered: lines 9 8(false) 11(true) 11 13(true) 13(true) 14 17
path 9 not covered: lines 9 8(false) 11(true) 11 13(true) 13(false) 16 17
path 10 not covered: lines 9 8(false) 11(true) 11 13(false) 16 17
path 11 not covered: lines 9 8(false) 11(false) 11 13(true) 13(true) 14 17
path 12 not covered: lines 9 8(false) 11(false) 11 13(true) 13(false) 16 17
1: 3:int main ()
-: 4:{
В этом режиме каждый непокрытый путь раскрывается с использованием строк и решений, как при --prime-paths-source, но выводится в одной строке. Этот режим даёт удобное общее представление о путях и позволяет отслеживать, как различные тесты и входные данные влияют на выполнение кода.
Счётчики выполнений являются накопительными. Если повторно запустить пример программы, не удаляя файл .gcda, количество выполнений каждой строки исходного кода будет добавлено к результатам предыдущих запусков. Это может быть полезно в разных ситуациях. Например, так можно накапливать данные за несколько запусков программы в рамках набора проверочных тестов или получать более точную долгосрочную статистику по большому числу запусков.
Данные в файлах .gcda сохраняются непосредственно перед завершением программы. Для каждого исходного файла, скомпилированного с параметром -fprofile-arcs, код профилирования сначала пытается прочитать существующий файл .gcda; если файл не соответствует исполняемому файлу (например, отличается количество базовых блоков), его содержимое игнорируется. Затем код добавляет новые счётчики выполнений и записывает данные в файл.
Можно создать отчёт для подмножества функций с помощью параметров --include и --exclude. Это особенно полезно в сочетании с --stdout: можно изучить поведение и покрытие конкретной функции, запустив тест, просмотрев вывод gcov, проверив другой набор входных данных и снова запустив gcov.
$ gcov -m --stdout --include inc tmp
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
2*: 8: void inc () { b++; }
------------------
Foo<char>::inc():
#####: 8: void inc () { b++; }
------------------
Foo<int>::inc():
2: 8: void inc () { b++; }
------------------
По умолчанию gcov сопоставляет имена с манглированными именами; это поведение можно изменить флагом -M. Обратите внимание, что сопоставление и вывод отчёта выполняются независимо: можно сопоставлять манглированные имена, выводя деманглированные, и наоборот. Чтобы создать отчёт для экземпляра int из Foo с сопоставлением манглированных и деманглированных имён:
$ gcov -t -m -M tmp --include 'Foo<int>'
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
1: 7: Foo(): b (1000) {}
2: 8: void inc () { b++; }
$ gcov -t -m tmp --include 'FooIi'
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
1: 7: Foo(): b (1000) {}
2: 8: void inc () { b++; }
Аргументы --include и --exclude являются расширенными регулярными выражениями (как grep -E), поэтому шаблон in.? соответствует и inc, и main. При использовании с -M ему также будут соответствовать все экземпляры int из Foo. Параметры --include и --exclude можно указывать несколько раз; если имя соответствует нескольким фильтрам, приоритет имеет последний совпавший фильтр. Например, чтобы включить main и экземпляр int из inc, исключив конструктор Foo:
$ gcov -t -m -M --include in --exclude Foo --include '<int>::inc' tmp
-: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
2: 8: void inc () { b++; }
1: 18:main (void)
-: 19:{
-: 20: int i, total;
1: 21: Foo<int> counter;
-: 22:
1: 23: counter.inc();
1: 24: counter.inc();
1: 25: total = 0;
-: 26:
11: 27: for (i = 0; i < 10; i++)
10: 28: total += i;
-: 29:
1*: 30: int v = total > 100 ? 1 : 2;
-: 31:
1: 32: if (total != 45)
#####: 33: printf ("Failure\n");
-: 34: else
1: 35: printf ("Success\n");
1: 36: return 0;
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-15.3.0/gcc/Invoking-Gcov.html