Здравствуйте, уважаемые читатели. Меня зовут Стас, я инженер команды DevOps Tooling в компании Align Technology. В этой статье я попробую коротко рассказать про то, как в нашей компании внедрили статический анализ кода на основе PVS-Studio.
Приблизительно год назад мы задумались о том, чтобы внедрить в нашей компании статический анализ. Мы и раньше использовали для этого различные средства, в том числе и для C/C++ проектов. Потому было интересно попробовать новый инструмент для известной задачи. Инструмент, более совершенный, чем встроенный средства VS, Cppcheck, интегрированный в Sonar.
Как такое всегда происходит, вначале возникла инициативная группа разработчиков (из одного разработчика, а его менеджер идею в целом поддержал). Он пришел к нашей команде и сказал, что хочет попробовать. Мы связались с разработчиками PVS студии, они предложили воспользоваться пробной бесплатной версией. Разработчик проверил код своего проекта и устроил для коллег-разработчиков презентацию, показав, какие ошибки помогает выявить PVS-Studio. Причем на презентации было показано, что и в относительно новом коде встречаются серьезные ошибки. Презентация была очень успешной, разработчики смеялись над кодом коллег и над своим кодом. Ряды энтузиастов пополнились, причем, что ценно, в том числе и менеджерами.
После этого менеджеры (совместо с этузиастами-разработчиками) посчитали ROI для внедрения этого продукта. Как они ROI считали — на этот счет, возможно, будет в дальнейшем отдельная статья. В общем, купили мы PVS-Studio. И перед нашей командой поставилась задача внедрить статический анализ и исправление ошибок в процесс разработки продукта.
Вот как мы эту задачу решили:
Процесс устранения выявленных предупреждений двигается рывками, в основном за счет энтузиастов. Время от времени, когда кто-то из инициативных разработчиков находит время, он устраняет какой-то один вид предупреждений. Это удобнее, потому что как правило, предупреждения легко устранимы без изменения логики кода, и потому подозрительные участки кода могут быть легко отрефакторены. Кроме того, многие предупреждения устраняются при помощи одного или нескольких более, или менее стандартных приемов, так что устранение одного типа предупреждений во всем проекте продуктивнее, чем, например, устранение всего разнообразия предупреждений в отдельном файле.
Для облегчения жизни разработчикам-этузиастам нами была произведена интеграция PVS-анализа в процесс CI. CI-сервером у нас служит Atlassian Bamboo, потому я буду использовать его термины при описании. Нами были созданы специальные сборочные планы, автоматически запускаемые по расписанию. По одному плану на каждый проект.
Этот план запускает через командную строку standalone-проверку головного проекта для актуальной версии кода. Анализ продолжается довольно долго (2-4 часа), потому такую проверку мы производим не чаще одного раза в день. После проверки PVS-анализатор генерирует plog-файл — по сути xml с перечнем найденных предупреждений. После этот файл немного обрабатывается (заменяются абсолютные пути на относительные), а потом, при помощи xslt-преобразования, превращается в xUnit-отчет, который умеет понимать и визуализовывать Bamboo. Мы превращаем каждый тип предупреждения в один юнит-тест, с перечнем всех файлов, в которых это предупреждение найдено. После первого запуска все предупреждения отправляются в карантин (то есть билды будут считаться успешными в случае проваленных тестов).
В тот момент, когда разработчик фиксит какой-то конкретный тип предупреждения, он выпускает соответствующий тест из карантина. Таким образом, если вдруг предупреждение, которое мы полагаем исправленным во всем проекте, вдруг появляется снова, мы получаем свалившийся билд и принимаем меры.
Выглядит свежеобнаруженное предупреждение так:
Стандартно xUnit содержит трехуровневую иерархию: TestSuite как объединение TestCase-ов. В TestCase содержится от 0 (если тест прошел) до множества Failures. Мы транслируем результаты проверки следующим образом.
При интеграции PVS-анализатора в наш CI возникло несколько трудностей.
Во-первых, трудности с лицензией. Нами была куплена site-лицензия на 30 пользователей. В соответствии с лицензионной политикой, одна установка на конкретный экземпляр ОС "съедает" одну лицензию. Поскольку сборок и проектов у нас много, то и сборочных агентов тоже прилично — мы подходим уже к лимиту в 100 агентов (сейчас их ~90). Конечно, не все из них используются для сборки C++-кода, однако и таких машин у нас достаточно. Поставить на них на всех PVS-студию мы не могли, пришлось сделать несколько выделенных под это агентов. В результате, если в момент запуска анализа эти выделенные агенты заняты, анализ ждет их освобождения и в результате занимает их на продолжительное время в первой половине рабочего дня. Из-за этого у нас health-check билды, стартуемые после каждого коммита, испытывают нехватку агентов. Кроме того, затрудняется автоматическое развертывание агентов (приходится все время пересчитывать используемые лицензии, что непросто в отсутствие лицензионного сервера).
Во-вторых, трудности с получением справочника предупреждений. Единственное место, откуда их можно получить — это сайт разработчиков PVS-студии, раздел технической документации. Однако сделать автоматический парсер этой страницы нам пока не удалось — верстка страницы слишком неудобная для правильного разбора и каждый раз немного меняется. Да и есть некоторая сложность автоматического централизованного обновления версий PVS-студии на всех агентах и справочника (это конечно можно решить, но пока не дошли руки). Так что обновляем пока руками.
Вот несколько примеров найденных ошибок, то есть когда процесс сработал в новом коде.
V544. It is odd that the value 'X' of HRESULT type is compared with 'Y'
static HRESULT findSomething(const X& x);
// later in the code
if (findSomething(x) == -1)
Minor issue: в этом месте проблема стиля
V501: There are identical sub-expressions to the left and to the right of the 'foo' operator
assert(leftPoints.size() == leftPoints.size());
Minor issue: неверный assert — no production impact
if (upper && upper)
return something;
Major issue: баг
template<class T>
void operator () ( const char* name, const T& t1, const T& t2 )
{
if( t1 != t1 )
{
diff = true;
}
}
Major issue: баг
V674: The expression contains a suspicious mix of integer and real types.
double precision = getSettingInt("Model", "Precision", 1e-6);
Major issue: баг
V655: The strings were concatenated but are not utilized. Consider inspecting the expression.
switch (someEnum)
{
case someCondition:
err + " is null ";
break;
V596: The object was created but it is not being used. The 'throw' keyword could be missing.
if (open(...))
{
std::runtime_error("Can not open database");
}
Minor issue: баг, который проявится с небольшой вероятностью
V557: Array overrun is possible.
float Jx[3];
...
for (int i=0; i<4; i++){
for (int j=i; j<4; j++){
value += Jx[i]*Jx[j];
}
}
Хотим заметить, что с недавнего времени в дистрибутиве PVS-Studio идет утилита PlogConverter. Она идет в исходниках и позволяет конвертировать .plog в .html, .txt, .csv или любой другой формат. На ее основе можно быстро сделать преобразование в свой любимый формат. С описанием PlogConverter можно познакомиться в соответствующем разделе документации.
Первоначальная статья была опубликована на сайте habrahabr.ru в блоге компании Align Technology. Компания Align Technology Inc - один из мировых лидеров в разработке инновационных медицинских технологий, базируйщийся в Кремниевой долине. Компания производит и успешно реализует на рынке уникальный продукт - устройство Invisalign, которое дает возможность людям приобрести идеальную улыбку простым и эстетичным способом (без традиционных брекетов).
0