Статический анализ кода позволяет выявлять и устранять многие дефекты на раннем этапе. Более того, можно обнаружить спящие ошибки, которые в момент появления никак не проявляют себя. Они могут доставить массу проблем в будущем, потребовав для своего обнаружения многих часов отладки. Рассмотрим пример такой спящей ошибки.
Чтобы показать преимущество регулярного использования статического анализатора PVS-Studio, мы настроили у себя регулярную проверку проекта Blender. Подробнее про эту идею мой коллега писал здесь.
Я временами посматриваю на предупреждения, сгенерированные для нового или изменённого кода Blender. Новые ошибки находятся регулярно, но большинство из них скучные или несущественные. Я, как рыбак, сижу и жду, когда будет что-то интересное, про что захочется написать. Сегодня как раз такой случай.
void UI_but_drag_set_asset(uiBut *but,
const AssetHandle *asset,
const char *path,
int import_type,
int icon,
struct ImBuf *imb,
float scale)
{
....
asset_drag->asset_handle = MEM_mallocN(sizeof(asset_drag->asset_handle),
"wmDragAsset asset handle");
*asset_drag->asset_handle = *asset;
....
}
Код должен выделить буфер в памяти, достаточный для хранения структуры типа AssetHandle. Такова была задумка программиста. Но на самом деле он выделяет буфер, равный не размеру структуры, а размеру указателя.
Ошибка здесь:
sizeof(asset_drag->asset_handle)
Правильный вариант:
sizeof(*asset_drag->asset_handle)
Анализатор выявил эту ошибку, выдав предупреждение: V568: It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'asset_drag->asset_handle' class object. interface.c 6192
Всё просто. Классический паттерн ошибки, с которым мы встречались в различных проектах. Интересно другое! Этот код сейчас работает правильно! Автору кода повезло. Посмотрим, что собой представляет структура AssetHandle:
typedef struct AssetHandle {
const struct FileDirEntry *file_data;
} AssetHandle;
Структура сейчас содержит в себе ровно один указатель. Получается, что размер структуры совпадает с размером указателя!
Перед нами красивая бомба замедленного действия. Этот код будет надёжно и стабильно работать годами. Он будет полностью работоспособен до тех пор, пока кто-то не захочет добавить в структуру новое поле.
В этот момент приложение сломается. Причём будет непонятно, что и где именно сломалось. Под структуру будет выделяться памяти меньше, чем требуется. Хорошо если программисту повезёт и при выходе за границу буфера возникнет Access Violation. Но, скорее всего, просто будет портиться какая-то память, и разработчика могут ждать часы мучительной отладки кода.
Используйте статический анализ кода, чтобы существенно улучшить качество и надёжность кода. Это полезно как в краткосрочной, так и в долгосрочной перспективе.
Статический анализ способен выявить не все ошибки. Однако преимуществ от его регулярного использования больше, чем затрат на ежедневный просмотр отчёта с новыми предупреждениями. Недавно в статье наш пользователь как раз описывал, как он пришёл к выводу, что лучше запускать анализатор, чем три дня отлаживаться.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. Static analysis protects your code from time bombs.
Комментарии (11)
anya_simba
04.08.2021 14:30-1У вас потрясающий продукт
Скажите, планируете когда-нибудь сделать подобное для TypeScript?
omlk2011
04.08.2021 20:53Необходимо писать так, чтобы можно было легко проверить результат:
unsigned int mem_malloc_size = sizeof(*asset_drag->asset_handle); // unsigned long mem_malloc_size = sizeof(*asset_drag->asset_handle); // size_t mem_malloc_size = sizeof(*asset_drag->asset_handle); asset_drag->asset_handle = MEM_mallocN(mem_malloc_size, "wmDragAsset asset handle");
Итоги:- Необходимо писать так, чтобы можно было легко проверить результат.
- Примерно понимать как можно устно посчитать размер структуры и как он еще зависит от выравнивания в памяти.
namee
06.08.2021 09:24Один из проектов на 2020 юнити проанализировал. Есть конечно вопросы, но в целом работает.
А вот проект на 2019 юнити (не знаю влияет ли версия) но .. анализатор грузит процы под 80% и висит в статусе подготовки к анализу. Таймаут анализа выключил. Ждал около часа. Ничего не произошло.
Есть какая-то возможность узнать что происходит?
foto_shooter
06.08.2021 15:51+1Можете написать нам в саппорт, пожалуйста? Хочется перевести общение туда, так как там будет удобнее общаться, выяснять какие-то детали. :)
Сразу прошу написать вопросы, которые у нас возникли по анализу проектов на Unity 2020 - интересно. Ну и с зависанием будем разбираться, конечно.
petropavel
06.08.2021 19:48Ну почему же часы мучительной отладки, сейчас же не 1990-й. ASAN поймает это сразу же, на присваивании.
Ловить анализатором во время компиляции, конечно, лучше. Но и с точки зрения отладки это не большая проблема.
zanzack
08.08.2021 11:51Хорошо если программисту повезёт и при выходе за границу буфера возникнет Access Violation. Но, скорее всего, просто будет портиться какая-то память
MS Visual Studio в режиме DEBUG умеет отлавливать выход за пределы массива.
namee
Доброго дня! В проекте юнити 2020 постоянно жалуется на дублирование библиотек в солюшне.
Наверняка известная проблема и как-то лечится?
LEVEL_0 jar:file:/C:/Users/*/.Rider2019.3/config/plugins/pvsstudio-rider-plugin/lib/pvsstudio-rider-plugin-7.13.48033.jar!/icons/icoFavoriteUnchecked.png V052 Error was encountered while trying to open solution file 'D:/*/Design.sln': The solution file has two projects named "UnityEngine.UI". D:\*\Design.sln No project files were found for analysis under the specified platform/configuration. Exception message: The solution file has two projects named "UnityEngine.UI". D:\*\Design.sln [] []
в sln - файле действительно дважды все модули прописаны. Если удалить вторую копию, то анализ срабатывает. Но при запуске юнити sln-файл возвращается в исходное продублированное состояние.
foto_shooter
Добрый день! :)
Да, есть такая проблема - более подробно я описывал это в отедльной заметке (причины проблемы и способы лечения). Если вкратце - в редакторе нужно отключить опцию "Player projects".
Вот сама заметка.
Пожалуйста, напишите, подошло ли Вам решение и помогло ли. :)
namee
Большое спасибо! Это работает.