Spec-Zone.ru › GCC 15

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API