0848_Timebomb_ru/image2.png
Статический анализ кода позволяет выявлять и устранять многие дефекты на раннем этапе. Более того, можно обнаружить спящие ошибки, которые в момент появления никак не проявляют себя. Они могут доставить массу проблем в будущем, потребовав для своего обнаружения многих часов отладки. Рассмотрим пример такой спящей ошибки.


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


  1. namee
    04.08.2021 13:51

    Доброго дня! В проекте юнити 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-файл возвращается в исходное продублированное состояние.


    1. foto_shooter
      04.08.2021 14:33
      +2

      Добрый день! :)

      Да, есть такая проблема - более подробно я описывал это в отедльной заметке (причины проблемы и способы лечения). Если вкратце - в редакторе нужно отключить опцию "Player projects".

      Вот сама заметка.

      Пожалуйста, напишите, подошло ли Вам решение и помогло ли. :)


      1. namee
        04.08.2021 14:47
        +3

        Большое спасибо! Это работает.


  1. anya_simba
    04.08.2021 14:30
    -1

    У вас потрясающий продукт
    Скажите, планируете когда-нибудь сделать подобное для TypeScript?


    1. Andrey2008 Автор
      04.08.2021 14:30
      +1

      Обязательно бахнем! Но потом. (с)


  1. 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");
    

    Итоги:
    • Необходимо писать так, чтобы можно было легко проверить результат.
    • Примерно понимать как можно устно посчитать размер структуры и как он еще зависит от выравнивания в памяти.


  1. namee
    06.08.2021 09:24

    Один из проектов на 2020 юнити проанализировал. Есть конечно вопросы, но в целом работает.

    А вот проект на 2019 юнити (не знаю влияет ли версия) но .. анализатор грузит процы под 80% и висит в статусе подготовки к анализу. Таймаут анализа выключил. Ждал около часа. Ничего не произошло.

    Есть какая-то возможность узнать что происходит?


    1. foto_shooter
      06.08.2021 15:51
      +1

      Можете написать нам в саппорт, пожалуйста? Хочется перевести общение туда, так как там будет удобнее общаться, выяснять какие-то детали. :)

      Сразу прошу написать вопросы, которые у нас возникли по анализу проектов на Unity 2020 - интересно. Ну и с зависанием будем разбираться, конечно.


  1. petropavel
    06.08.2021 19:48

    Ну почему же часы мучительной отладки, сейчас же не 1990-й. ASAN поймает это сразу же, на присваивании.

    Ловить анализатором во время компиляции, конечно, лучше. Но и с точки зрения отладки это не большая проблема.


  1. zanzack
    08.08.2021 11:51

    Хорошо если программисту повезёт и при выходе за границу буфера возникнет Access Violation. Но, скорее всего, просто будет портиться какая-то память

    MS Visual Studio в режиме DEBUG умеет отлавливать выход за пределы массива.


    1. Andrey2008 Автор
      20.10.2021 21:47

      Это смотря куда попасть :)