Upload
platonov-sergey
View
707
Download
3
Embed Size (px)
Citation preview
Как мы уменьшили количество ошибок в Unreal Engine
с помощью статического анализа кода
Евгений Рыжков
ООО «СиПроВер»
www.viva64.com
1/26
Структура доклада
• Этапы внедрения анализатора кода в живой проект.
• Примеры ошибок из кода Unreal Engine.
• Типы исправлений в коде.
• Зачем вообще знакомиться со статическим анализом.
2/26
Этапы внедрения статического анализатора кода
3/26
Этап 0. Научить инструмент проверять проект• Убедиться, что весь код компилируется
• Убедиться, что весь код корректно проверяется анализатором
4/26
Пример ошибки из кода Unreal Engine
if (*Buffer == TCHAR('\"')) {
while (*Buffer &&
*Buffer != TCHAR('\"') &&
*Buffer != TCHAR('\n') &&
*Buffer != TCHAR('\r'))
{
Buffer++;
}
V637 Two opposite conditions were encountered. The second condition is always false. Check lines: 310, 312. propertystruct.cpp 310
5/26
Этап 1. Борьба с имеющимися сообщениями• Запустили анализатор на существующем проекте – получили 2000
сообщений.
• Бросаться их сразу править – плохая идея. Менеджер проекта негодует.
• Оставить как есть – не увидите новых сообщений.
• Обязательно в инструменте должна быть возможность скрыть этим сообщения. Где-то нет такой возможности? Не тот инструмент.
6/26
Пример ошибки из кода Unreal Engine
V612 An unconditional 'return' within a loop. unrealaudiotestgenerators.cpp 49
static float WrapTwoPi(float Value)
{
while (Value >= TWO_PI)
{
return Value -= TWO_PI;
}
while (Value < 0)
{
return Value += TWO_PI;
}
return Value;
} 7/26
Этап 2. Настройка регулярного запуска на билд-сервере (и на машинах разработчиков)
• Абсолютно необходимая вещь – это настройка запуска на билд-сервере.
• Надо давать всем разработчикам возможность ознакомиться с результатами проверки.
• В идеале регулярный запуск нужен не только на билд-сервере по ночам, но и на машинах разработчиков во время работы.
8/26
void FillUpTransformBasedOnRig(....)
{
....
const URig* Rig = Skeleton->GetRig();
int32 NodeNum = Rig->GetNodeNum();
if (Rig && NodeNum > 0)
{
....
}
....
}
Пример ошибки из кода Unreal Engine
V595 The 'Rig' pointer was utilized before it was verifiedagainst nullptr. Check lines: 1844, 1846. animsequence.cpp 1844
9/26
Этап 3. Правим новые ошибки
• Убедить разработчиков править все новые ошибки, чтобы было 0 сообщений.
• Здесь инструменты статического анализа могут предложить только плетку менеджеру проекта, к сожалению.
10/26
Пример ошибки из кода Unreal Engine
V579 The Memcmp function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. pimplrecastnavmesh.cpp 172
bool FRecastQueryFilter::IsEqual(
const INavigationQueryFilterInterface* Other) const
{
return FMemory::Memcmp(this, Other, sizeof(this)) == 0;
}
11/26
Этап 4. Правим старые ошибки
• Очень желательно все-таки победить старые ошибки.
• Поговорим про скорость правки ошибок.
12/26
Ожидаемая скорость правки ошибок
• Количество ошибок уменьшается равномерно с каждым рабочим днём.
13/26
Фактическая скорость правки ошибок
• На самом деле, сообщения исчезают в начале быстрее, чем потом.
14/26
На что ушли 17 рабочих дней в UE?Количество предупреждений анализатора в различные дни
Сначала правятся легкие ошибки, потом посложнее и самые сложные в конце
15/26
На что ушли 17 рабочих дней в UE:расшифровка• С нашей стороны работало 2 программиста.
• Код UE нам совершенно не знаком.
• Коммуникации с программистами заказчика затруднены
(Hello, USA TimeZone).
16/26
Старые ошибки поправлены! Можно расслабиться?
17/26
Пример ошибки из кода Unreal Engine
V561 It's probably better to assign value to 'Existing' variable than to declare it anew. Previousdeclaration: streamablemanager.cpp, line 325. streamablemanager.cpp 332
FStreamable* Existing = StreamableItems.FindRef(TargetName);
....
if (!Existing)
{
TargetName = ResolveRedirects(TargetName);
FStreamable* Existing = StreamableItems.FindRef(TargetName);
}
if (Existing && Existing->bAsyncLoadRequestOutstanding)
....
18/26
Типы найденных ошибок (не код)
• Настоящие ошибки.
• «Запахи».
• Правки, чтобы «угодить» анализатору кода.
19/26
«Пахнущий код»: ошибки нет, но анализатор прав
if (InitializationState == Working)
{
//bool AllSuccessful = true;
//Insert other dependencies here
if (InitializationState == Working
/* && AllSuccessful */)
{
InitializationState = Success;
}
}
V571 Recurring check. This condition was already verified in line 357. questionblock.cpp 363 20/26
Примеры ошибок, которые появились "на наших глазах"
21/26
Пример ошибки из кода Unreal Engine
virtual FString ToString() const override
{
if (Token.TokenType == FBasicToken::TOKEN_Identifier ||
FBasicToken::TOKEN_Guid)
{
....
}
V560 A part of conditional expression is always true: FBasicToken::TOKEN_Guid. k2node_mathexpression.cpp 235
22/26
Пример ошибки из кода Unreal Engine
static void GetArrayOfSpeakers(....)
{
Speakers.Reset();
uint32 ChanCount = 0;
for (uint32 SpeakerTypeIndex = 0;
SpeakerTypeIndex < ESpeaker::SPEAKER_TYPE_COUNT,
ChanCount < NumChannels; ++SpeakerTypeIndex)
....
}
V521 Such expressions using the ',' operator are dangerous. Make sure the expression is correct. unrealaudiodevicewasapi.cpp 128
,
23/26
Выводы по результатам проверки Unreal Engine• Код проекта Unreal Engine весьма качественный.
• Править чужой незнакомый код часто очень сложно. Свой –намного легче.
• Скорость "переработки" предупреждений не линейна.
• Максимальная польза от статического анализа может быть получена только при его регулярном использовании.
24/26
Пара слов о программистской культурев России и мире, или «Зачем вообще знакомиться со статическим анализом?»
• Люди на западе применяют эту практику давно и успешно.
• Знание принципов и инструментов статического анализа кода дает +10 на собеседовании разработчика и +20 при внедрении в своем проекте. И должность Team Leader в придачу.
25/26
Q&A
• Написать письмо: [email protected]
• Подписаться на твиттер: https://twitter.com/Code_Analysis
• Зайти на сайт: www.viva64.com
• Подойти на конференции и спросить что-то (в большинстве случаем мы не кусаемся).
26/26