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% покрытие?


person Matt    schedule 11.05.2010    source источник
comment
Полезно ли вообще содержимое файла .gcda?   -  person Cascabel    schedule 11.05.2010


Ответы (4)


Ого! Дамп сборки bde показывает, что эта версия GCC компилирует этот оператор switch как некоторую аппроксимацию двоичного дерева, начиная с середины набора. Таким образом, он проверяет, равно ли i 2, затем проверяет, больше ли оно или меньше 2, а затем для каждой стороны он проверяет, равно ли оно 1 или 3 соответственно, а если нет, то он переходит в значение по умолчанию.

Это означает, что есть два разных пути кода для получения результата по умолчанию - один для чисел больше 2, которые не являются 3, и один для чисел меньше 2, которые не являются 1.

Похоже, вы достигнете 100% покрытия, если измените i<4 в своем цикле на i<=4, чтобы проверить путь с каждой стороны.

(И да, это то, что, скорее всего, изменилось с GCC 3.x на GCC 4.x. Я бы не сказал, что это «исправлено», поскольку это не «неправильно», если не считать того, что результаты gcov сбивают с толку. просто на современном процессоре с предсказанием ветвления он, вероятно, медленный, а также слишком сложный.)

person Brooks Moses    schedule 13.05.2010

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

person WildCrustacean    schedule 11.05.2010
comment
Я сомневаюсь, что gcov делает провальную ветку; похоже, что это делает gcc. Что будет, если включить оптимизацию? - person Brooks Moses; 12.05.2010
comment
Нет ничего плохого в провале для default: в конце концов, может быть case ниже, чтобы избежать выполнения кода, специфичного для default. Что плохого, так это то, что последний оператор switch проваливается, потому что не во что упасть. - person Matthieu M.; 12.05.2010
comment
@Brooks Включение оптимизации не меняет проблемы, и похоже, что если я использую -O3 или выше, добавляются новые ветки. - person WildCrustacean; 12.05.2010
comment
@Matthieu Я согласен, это имеет больше смысла. Я отредактировал свой ответ, чтобы отразить это. - person WildCrustacean; 12.05.2010
comment
@bde: Ну ладно. Вот вам и идея! - person Brooks Moses; 12.05.2010
comment
@Brooks: Это была хорошая идея. Я опубликовал отрывок из кода сборки в своем ответе, может быть, кто-нибудь, кто знает больше о gcc, сможет объяснить, что происходит. - person WildCrustacean; 13.05.2010
comment
@bde: Спасибо! Кажется, что этот дамп сборки действительно кое-что объясняет; см. ответ, который я опубликовал. - person Brooks Moses; 13.05.2010

Вы уверены, что у вас аут? Вот мои результаты (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'
person ergosys    schedule 11.05.2010
comment
он сказал ran "a", что, на мой взгляд, предполагает, что он запускал a.exe и использует окна. - person nategoose; 11.05.2010
comment
Полагаю - по умолчанию я ассоциирую gcc с unix. Результаты выглядели согласующимися с случайным запуском другой версии исполняемого файла. - person ergosys; 12.05.2010
comment
Да, я использую MinGW в Windows, это gcc 3.4.5. Может ли это быть исправлено в более поздних версиях gcc? - person Matt; 12.05.2010

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

person Matt    schedule 08.10.2010