Ответ 1
Я использую mingw на windows (который не является последним gcc), и похоже, что это может быть отсортировано в более новых версиях gcc.
Я запускаю gcov над некоторым кодом C с помощью оператора switch. Я написал тестовые примеры, чтобы охватить все возможные пути через этот оператор switch, но он по-прежнему сообщает ветку в выражении switch как не принятую и менее 100% в статусе "Взято по крайней мере один раз".
Вот пример кода для демонстрации:
#include "stdio.h"
void foo(int i)
{
switch(i)
{
case 1:printf("a\n");break;
case 2:printf("b\n");break;
case 3:printf("c\n");break;
default: printf("other\n");
}
}
int main()
{
int i;
for(i=0;i<4;++i)
foo(i);
return 0;
}
Я построил с помощью "gcc temp.c -fprofile-arcs -ftest-coverage
", запустил "a
", затем сделал "gcov -b -c temp.c
". На выходе отображается восемь ветвей на коммутаторе, а одно (ответвление 6) не принято.
Каковы все эти отрасли и как я могу получить 100% -ный охват?
Я использую mingw на windows (который не является последним gcc), и похоже, что это может быть отсортировано в более новых версиях gcc.
Ого! bde assembly dump показывает, что эта версия GCC компилирует этот оператор switch как некоторую аппроксимацию двоичного дерева, начиная с середины набора. Поэтому он проверяет, равен ли i
2, затем проверяет, больше ли оно или меньше 2, а затем для каждой стороны проверяет, равно ли оно 1 или 3 соответственно, а если нет, то оно переходит в значение по умолчанию.
Это означает, что для получения результата по умолчанию есть два разных пути кода: один для чисел выше 2, которые не являются 3, а один для чисел ниже 2, которые не являются 1.
Похоже, вы попадете на 100% -ый охват, если вы измените этот i<4
в своем цикле на i<=4
, чтобы проверить путь с каждой стороны.
(И да, это то, что, скорее всего, изменилось с GCC 3.x на GCC 4.x. Я бы не сказал, что это "исправлено", так как это не "неправильно", так как это не относится к результатам gcov запутанный. Это просто на современном процессоре с предсказанием ветвлений, он, вероятно, медленный, а также слишком сложный.)
Я получаю тот же результат, используя gcc/gcov 3.4.6.
Для оператора switch он обычно должен генерировать две ветки для каждого оператора case. Один из них - это случай, когда дело истинно и должно выполняться, а другое - ветвь "прорыв", которая переходит к следующему случаю.
В вашей ситуации, похоже, что gcc делает "прорывную" ветвь для последнего случая, что не имеет смысла, поскольку в нее нечего впасть.
Здесь выдержка из кода сборки, сгенерированного gcc (я изменил некоторые метки для удобочитаемости):
cmpl $2, -4(%ebp)
je CASE2
cmpl $2, -4(%ebp)
jg L7
cmpl $1, -4(%ebp)
je CASE1
addl $1, LPBX1+16
adcl $0, LPBX1+20
jmp DEFAULT
L7:
cmpl $3, -4(%ebp)
je CASE3
addl $1, LPBX1+32
adcl $0, LPBX1+36
jmp DEFAULT
Я признаю, что я мало разбираюсь в сборке x86, и я не понимаю использование метки L7, но это может иметь какое-то отношение к дополнительной ветки. Возможно, кто-то, у кого больше знаний о gcc, может объяснить, что здесь происходит.
Похоже, что это может быть проблема с более старой версией gcc/gcov, обновление до более новой версии gcc/gcov может решить проблему, особенно учитывая, что другая публикация выглядит корректно.
Вы уверены, что используете a.out? Вот мои результаты (gcc 4.4.1):
File 't.c'
Lines executed:100.00% of 11
Branches executed:100.00% of 6
Taken at least once:100.00% of 6
Calls executed:100.00% of 5
t.c:creating 't.c.gcov'