Технический термин для блока в покрытии кода: базовый блок .Для шпаргалки непосредственно из записи в Википедии :
Код в базовом блоке имеет одну точку входа, что означает, что ни один код внутри него не является назначением инструкции перехода где-либо в программеи имеет одну точку выхода, то есть только последняя инструкция может заставить программу начать выполнение кода в другом базовом блоке.В этих условиях всякий раз, когда выполняется первая инструкция в базовом блоке, остальные инструкции обязательно выполняются ровно один раз, по порядку.
Базовый блок важен в покрытии кода, потому что мы можем вставить датчик в начале основного блока.Когда этот зонд попадет, мы знаем, что все следующие инструкции в этом базовом блоке будут выполнены (из-за свойств базового блока).
К сожалению, с компиляторами (и особенно с оптимизацией) этоНе всегда очевидно, как исходный код отображается на основные блоки.Самый простой способ узнать это - посмотреть на сгенерированную сборку.Например, давайте посмотрим на ваш исходный main
& testfunction
:
Для main
я вижу сборку ниже (с чередованием с исходным источником).Аналогично тому, что Питер делает здесь , я отметил, где начинаются базовые блоки.
int main()
{
013B2D20 push ebp <--- Block 0 (initial)
013B2D21 mov ebp,esp
013B2D23 sub esp,40h
013B2D26 push ebx
013B2D27 push esi
013B2D28 push edi
testfunction(-1);
013B2D29 push 0FFFFFFFFh
013B2D2B call testfunction (013B10CDh)
013B2D30 add esp,4 <--- Block 1 (due to call)
testfunction(1);
013B2D33 push 1
013B2D35 call testfunction (013B10CDh)
013B2D3A add esp,4 <--- Block 2 (due to call)
}
013B2D3D xor eax,eax
013B2D3F pop edi
013B2D40 pop esi
013B2D41 pop ebx
013B2D42 mov esp,ebp
013B2D44 pop ebp
013B2D45 ret
Мы видим, что main
имеет три основных блока: один начальный блок и два другихиз-за вызовов функций.Глядя на код, это кажется разумным.testfunction
немного сложнее.Просто глядя на источник, кажется, что есть три блока:
- Запись в тест функции и логики (
input > 0
) - Условие истинной ветви (
return 1
) - Условие ложной ветви (
return 0
)
Однако из-за фактической сгенерированной сборки существует четыре блока.Я предполагаю, что вы создали свой код с отключенной оптимизацией.Когда я собираюсь с VS2010 в конфигурации Debug (оптимизация отключена), я вижу следующую разборку для testfunction
:
int testfunction(int input)
{
013B2CF0 push ebp <--- Block 0 (initial)
013B2CF1 mov ebp,esp
013B2CF3 sub esp,40h
013B2CF6 push ebx
013B2CF7 push esi
013B2CF8 push edi
if (input > 0) {
013B2CF9 cmp dword ptr [input],0
013B2CFD jle testfunction+18h (013B2D08h)
return 1;
013B2CFF mov eax,1 <--- Block 1 (due to jle branch)
013B2D04 jmp testfunction+1Ah (013B2D0Ah)
}
else {
013B2D06 jmp testfunction+1Ah (013B2D0Ah) <--- Not a block (unreachable code)
return 0;
013B2D08 xor eax,eax <--- Block 2 (due to jmp branch @ 013B2D04)
}
}
013B2D0A pop edi <--- Block 3 (due to being jump target from 013B2D04)
013B2D0B pop esi
013B2D0C pop ebx
013B2D0D mov esp,ebp
013B2D0F pop ebp
013B2D10 ret
Здесь у нас есть четыре блока:
- запись в функцию
- Условие истинной ветви
- Условие ложной ветви
- Общая функция epilog (очистка стека и возврат)
Если бы компилятор продублировал эпилог функции как в условиях true, так и в условии false ответвлений, вы бы увидели только три блока.Также, что интересно, компилятор вставил ложную инструкцию jmp
в 013B2D06
.Поскольку это недоступный код, он не рассматривается как базовый блок.
В целом весь этот анализ является излишним, поскольку общая метрика покрытия кода скажет вам, что вам нужно знать.Этот ответ был просто для того, чтобы подчеркнуть, почему количество блоков не всегда очевидно или что ожидается.