gcov и операторы переключения

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

12
задан Nathan Fellman 12 May 2010 в 10:50
поделиться

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

2
ответ дан 2 December 2019 в 23:06
поделиться

Вы уверены, что используете 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'
1
ответ дан 2 December 2019 в 23:06
поделиться

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

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

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

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

4
ответ дан 2 December 2019 в 23:06
поделиться