Подтвердить что ты не робот

Операторы gcov и switch

Я запускаю 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% -ный охват?

4b9b3361

Ответ 1

Я использую mingw на windows (который не является последним gcc), и похоже, что это может быть отсортировано в более новых версиях gcc.

Ответ 2

Ого! 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 запутанный. Это просто на современном процессоре с предсказанием ветвлений, он, вероятно, медленный, а также слишком сложный.)

Ответ 3

Я получаю тот же результат, используя 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 может решить проблему, особенно учитывая, что другая публикация выглядит корректно.

Ответ 4

Вы уверены, что используете 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'