Про статический анализ С++
Тут меня навели на Coverity SCAN, которую дают помацать опенсорсным проектам на халяву.
Ну я посабмитил туда LibRaw, сегодня пустили, результаты оказались удивительными:
107 предупреждений, из них где-то треть - с High Impact.
Из High Impact:
- Штук 10 в Microsoft STL (я самбитил виндовую сборку)
- Еще какое-то количество такого примерно вида:
И на это дается предупреждение, дескать неаккуратненько, не инициализированная переменная. Я с ним согласен по общим ощущениям, так не надо делать. Но в реальной жизни бывает два вида layout - и это явно прописано в вызывающем коде. Т.е. у машинки достаточно данных, чтобы сообразить, что это не 'High impact', а просто неаккуратненько.int variable;
if(layout==Layout1) variable=value1;
if(layout==Layout2) variable=value2; - Некоторое количество предупреждений, дескать unsigned short при расширении до 32-64 бит может больно укусить. С этим я не хочу спорить - формально машинка права, а по факту в этих unsigned short живут размеры картинки и до 32767 они в ближайшие годы не дорастут.
Т.е. опять, фиксить не надо - в случае данной предметной области.
- Все остальные найденные 'High Impact' проблемы - это просто ложные срабатывания. Т.е. код, согласен, не идеальный (видели бы вы этот код из dcraw !), но все найденное - не является ошибкой.
В частности, любимый коффиновский метод проверки диапазона значений 0..N одним действием машинка просто не чует:
Я сам ТАК не пишу, но читаю и считаю что прием, как минимум, надо знать и в чужих исходниках распознавать.int var=-1;
if(bla-bla) { var = some_non_negative_value; }
if( (unsigned)var < 5) // => это проверяет на диапазон 0..4Но кроме этих ложных срабатываний - еще куча других, тоже ложных.
Ну и по моему опыту, Static Analysis, примененный эпизодически, в большинстве случаев дает очень плохое отношение сигнал/шум. Наверное, если заниматься систематически, используя систему, где можно подавить предупреждения в конкретных местах, чтобы при сабмите следующей версии - весь этот шум не повторялся, польза может быть. Да и то....
Вопросы у меня такие:
- А кто-нибудь использует статический анализ по смыслу (а не из соображений, что CTO/PM велел, чтобы предупреждений не было, вот мы и дуем на воду)?
- А в компактных проектах, которые ведутся очень небольшим количеством опытных девелоперов? (я опять же понимаю, что в большом проекте статический анализатор наводит орднунг, от которого польза..)
- И если на оба вопроса ответ "да" - то есть ли польза от потраченного времени? Ну вот если потрачу я сейчас час-другой на расстановку галок "это - не ошибка, а специально так сделано" - стоит ли ожидать пользу в дальнейшем?
Comments
не то что бы я занимался. даже не то что бы мне давали пощуп
не то что бы я занимался.
даже не то что бы мне давали пощупать.
но у меня сложилось впечетление, что gcc со всеми максимальными ворнингами дает результаты, сравнимые со всеми системами статического анализа.
ну и так же ложно срабатывает, да. примерно в тех же местах.
Ну для gcc-то несложно подправить исходник так, чтоб больше
Ну для gcc-то несложно подправить исходник так, чтоб больше не ругался.
И ведь помогает, зараза такая, только на прошлой неделе долго ругал его за чрезмерную осторожность, пока не сообразил, что он прав, а у меня ошибка.
как-то не хочется из-за его паранойи лепить бессмысленную ин
как-то не хочется из-за его паранойи лепить бессмысленную инициализацию
Не, по смыслу оно право - если когда-нибудь появится Layout3
Не, по смыслу оно право - если когда-нибудь появится Layout3, то хорошо бы иметь переменную инициализированной хоть как-то.
Это правильно, в смысле что "хороший тон".
Но в данном наборе исходников - ошибки нет, да.
я не про конкретно этот код. а про случаи типа когда первая
я не про конкретно этот код.
а про случаи типа когда первая итерация цикла гарантированно инициализирует переменную, использующуюся только после этого
а в твоем случае надо просто переписать через case и enum и
а в твоем случае надо просто переписать через case и enum и варнинга не будет.
Это чужой (contributed) код, пальцем не дотронусь!
Это чужой (contributed) код, пальцем не дотронусь!
отправь патчи в апстрим! кстати, достаточно просто enum
отправь патчи в апстрим!
кстати, достаточно просто enum
Нет никакого апстрима. Это именно contributed, конкретно в L
Нет никакого апстрима. Это именно contributed, конкретно в LibRaw.
Я с благодарностью такие куски кода принимаю, но что я при этом думаю - я никому не скажу!
повторюсь -- достаточно определить layout и Layout1 через ен
повторюсь -- достаточно определить layout и Layout1 через енум, после этого даже ифы работают
а я уже привык везде лепить, только из-за этого варнинга пог
а я уже привык везде лепить, только из-за этого варнинга поганого. Проще уже в переменную что-то сунуть, чем каждый раз мусор на экране смотреть..
Да-да-да, у меня такое тоже бывало, что думаешь "от какая ца
Да-да-да, у меня такое тоже бывало, что думаешь "от какая цаца, не нравицца ему видите-ли! да идиты нафег!", а потом через какое-то "ух-ёёёё..." и дальше эхом от стен "мать-мать-мать..." )))
gcc с -Wall ложно срабатывает чаще, чем этот Coverity. Т.е.
gcc с -Wall ложно срабатывает чаще, чем этот Coverity.
Т.е. предупреждений будет не 107, а бешеные мильены на конкретном коде.
а, вот в таком разе я особо не сравнивал, но мне показалось
а, вот в таком разе я особо не сравнивал, но мне показалось что примерно одинаково, все же.
Есть такое мнение про системы
Есть такое мнение про системы статического анализа (да и вообще про все отладочно-дебажные тулзы, про тот же valgrind) - их нужно использовать более-менее регулярно, на всём процессе разработки.
Всякие очепятки, которые пропустил компилятор и которые приводят к фактическим ошибкам, рано или поздно повылазят, их разработчик самостоятельно пофиксит, без статических чекеров, просидев часик-другой в отладчике.
В результате такая ситуация и возникает - на практически готовый продукт натравливают всякий lint, он показывает ересь, бред и ложные срабатывания, потому, что разраб уже всё пофиксил.
Ну то есть идея в том, что -
Ну то есть идея в том, что
- при регулярном использовании мы обнаружим багу *раньше* и потратим на ее исправление меньше сил
- а на готовом проекте - смысла мало, т.к. он уже работает и доля ложных срабатываний будет слишком большой?
Интересная мысля, надо ее подумать.
> Но в реальной жизни бывает два вида layout - и это явно пр
> Но в реальной жизни бывает два вида layout - и это явно прописано в вызывающем коде. Т.е. у машинки достаточно данных, чтобы сообразить, что это не 'High impact', а просто неаккуратненько.
Э, нет, батенька, не согласен я с вами тут.
Это сегодня явно прописано строго два варианта вызова и никаких больше. А завтра в проект придёт новый чувак, которому поставят задачу сделать третий лейяут и он имеет полное право не знать о таком неявном свойстве кода, посему будет честное обращение к неинициализированной памяти.
Это очень правильный ворнинг, имхо. Это не "неаккуратненько", это мина на будущее расширение.
Я бы переписал тот код как свитч с throw exception по дефолту.
> С этим я не хочу спорить - формально машинка права, а по факту в этих unsigned short живут размеры картинки и до 32767 они в ближайшие годы не дорастут.
))) "640кб хватит всем!", "до 2000-го года ещё много лет, успеем поправить!" :)
Ну, т.е. понятно, что машина-то всех тонкостей domain specific не знает, посему имхо тоже вполне правильно ругается.
> В частности, любимый коффиновский метод проверки диапазона значений 0..N одним действием машинка просто не чует:
А чо-как ругается? Вполне нормальный метод, имхо...
По вопросу: я специально не использую, но обычно ставлю студию на наивыший уровень ворнингов и если что возникает - стараюсь или переправлять, или если однозначно в данном случае не нужно - подавлять. Несколько раз меня эти варнинги ОЧЕНЬ спасали от тяжелого и трудноуловимого дебуга, поэтому просто принял для себя решение по возможности сразу писать правильно и таки прислушиваться в варнингам. Хотя специально отдельными тулзами обычно не пользуюсь. Согласен с тем, что в идеале это надо делать системным образом, но по моему опыту даже вот такое эпизодическое следование мне лично здорово помогало.
1) Ну я согласен с тем, что так писать не надо. Но в том вид
1) Ну я согласен с тем, что так писать не надо. Но в том виде, в каком оно написано - это не ошибка и это видно если анализировать не только саму функцию, но и окружение.
2) Про unsigned short - я написал, что согласен именно по той причине, что domain specific неизвестен.
3) Про диапазон: дальше эта самая (unsigned)var < 5 используется для индексации массива - и ругается оно, что отрицательный индекс у массива. Ну, в тех местах, которые видел.
Мы используем, тот же Coverity. False positive'ов много, и в
Мы используем, тот же Coverity. False positive'ов много, и вычищать было мучительно, но по ощущениям - польза от этого действия есть. Глупости всякие успешно отлавливает, вроде вот таких - http://trac.nginx.org/nginx/changeset/4937/nginx.
А как народ умудряется без
А как народ умудряется без -Wall хотя бы иногда (а лучше еще и -pedantic для верности)? Я недавно вылавливал совершенно адовые ошибко-опечатки, которые глазами трудно заметить, типа "for (...;;<условие>)" или какие-то ошибки в snprintf-спецификаторах. Ну, можно, наверное, идеально писать, но это же не всем дано. Но это и правда не часто как-то нужно.
А вод динамический valgrind, в режиме проверки памяти/текущих ресурсов - прекрасен. Ну, опять же, кто-то, наверное, делает все c RAII, аккуратненько и с первого раза, но я так не умею, от первого знакомства с валгриндом вообще аж вспотел, настолько много всего текло и вообще удивительно как работало
Так ведь при тестировании все
Так ведь при тестировании все вылезет.
Конкретно в LibRaw - куча унаследованного кода, который на Wall дает миллиарды триллионов ложных срабатываний, а править эти места не хочется. В частности по той причине, что много кода генерируется именно из dcraw.c, который мержится средствами git. Если в моей копии поправить - мержиться перестанет.
Ну и с Valgrind/подобными - тоже ведь не так просто. Я пользуюсь Intel Inspector (по смыслу то же самое). Да, что-то он ловит, но я вот скажем аллоцированное на старте программы (гарантированно один раз) на финише обычно не освобождаю, ибо незачем. Оно это ловит и правильно ловит, но с моей точки зрения - это false positive.
> Так ведь при тестировании
> Так ведь при тестировании все вылезет.
Вот конкретно в этом for-е и printf-е все как-то работало, цикл рвался внутри еще по одному условию, а в принт не приходили данные больше 32 бит, то есть ошибки увидел только с -Wall. Да, достаточно редкие ошибки, и у нас почти такая же история - есть чужие .h-файлы которые выдают кучу нелепых варнингов. Но с -Wall -pedantic пытаюсь собрать хотя бы раз в месяц, мало ли чего?
> Оно это ловит и правильно ловит, но с моей точки зрения - это false positive.
Валгринд, кстати, меня тоже в некоторых местах раздражал, казалось что слишком уж умничает железяка чертова. Но потом, подумав, понял что правильно он возмущается. Теперь стараюсь как-то его умиротворить. В некоторых местах, само собой, не получается, BerkeleyDB например что-то там не инициализирует, pthreads текут и т.п., но по возможности стараюсь чтоб было без предупреждений
Меж тем - этот самый
Меж тем - этот самый интеловский анализатор я тут пытался использовать для поиска утечки хендлов файловых.
Нихрена оно не нашло, пришлось таки в отладчике подозрительные места пошагово проходить и думать. А что находит - наоборот ненужное.
а ты отечественный продукт, у них еще радуга в логотипе и мн
а ты отечественный продукт, у них еще радуга в логотипе и много постов на хабре, не пробовал? вроде не так плох
собстно насколько я слабо понимаю как они работают, они не делают предметного анализа или на входных данных. они просто парсят чем-то компилятороподобным код в дерево, а потом ищут в этом дереве известные им паттерны, не учитывая диапазон данных и т.д.
насчет того, что это шум я не согласен, если честно, это как миннимум плохой стиль.
большой практики с плюсами и анализаторами у меня нет, но вот с перлом чего мы только не находили благодаря перл-критику :)
Не. Я бы и эту не пробовал - но на меня надавили из diigiKam
Не.
Я бы и эту не пробовал - но на меня надавили из diigiKam (куда LibRaw импортится) - форвардили репорты, которые никуда не годные потому что кривые.
Ну я и решил сам попробовать - и мне не понравилось. Возможно, если перед каждой компиляцией это прогонять, то многие глупые опечатки будут половлены. А на оттестированном продукте сигнал-шум какой-то негодный получается.
Ну собственно да нормальный цикл использования таких штук
Ну собственно да нормальный цикл использования таких штук это до/после компиляции или хук на коммит
для С++ с его указателями гораздо полезнее valgrind, который
для С++ с его указателями гораздо полезнее valgrind, который может проверять очень многие вещи.
в целом, я скорее за статические анализаторы, ибо они дисциплинируют. ложные срабатывания можно отметить (а можно немного переписать код), но даже если 1 из 10 срабатываний не ложное, это хороший результат.
главное же средство борьбы с багами было и есть: простой и понятный код, покрытие тестами, грамотный логгинг, специальный дебаг режим с пред и постусловиями.
Хо-хо, тут через последний
Хо-хо, тут через последний clang прогнали FreeBSD и понаходили вот такого:
struct X { ... }
struct X *obj;
memset(obj, 0, sizeof(obj));
И это после многих лет жизни с gcc -Wall -Werror
И судя по всему - это
И судя по всему - это нисколько не мешало жить!
Особенно в коде Кербероса. Но
Особенно в коде Кербероса.
Но вообще -- вот вам и -Wall у gcc.
clang
Добавлю, что прогонять проект через clang, как источник дополнительных warning'ов - действительно полезно. Тем более он там так красиво всё вырисовывает.
В принципе ты прав, цель
В принципе ты прав, цель таких тулов - ловить 1-2 новых багов в неделю, а не 100 старых багов на рабочем коде.
Но тут еще пару интересных моментов есть:
1. В этой проге 80 чеккеров на С/С++, и в каждом из них штук 5-10 конфигурабильных опций. Чтобы получить максимум пользы надо как правило потратить пару дней на настройки под конкретный кодбейс - тогда ложных будет процентов 10-15.
В SCAN подкрутить тебе ничего не дали, поэтому результаты - какие есть. Считай ты примерил брюки рандомального размера.
2. -Wall и Coverity разные вещи, и одно другому не мешает. Компайлер тебе не скажет где ты забыл mutex взять или память освободить, а Coverity не будет ругаться если ты налепишь 20 переменных которые нигде не используются.
3. Есть разница между False Positive и Intentional. Если то что прога горовит тупо не правда - к примеру, что у тебя утекает память а утечки нет и быть не может - это одно. А если она тебе говорит что variable может быть не инициализированным в твоем коде, то это не то что неаккуратно - в коде баг однозначно есть, но информация вне кода этот баг делает для тебя маловажным. В таком случае этот баг можно отметить как Intentional и прога его будет всегда игнорировать. А если совсем раждражает, этот конкретный чеккер можно вырубить :)
Тут вот же проблема: что
Тут вот же проблема: что coverity (даже только этап сбора, без всасывания на веб), что всякие другие, которые пробовал - они же все какие-то невероятно тормознутые.
Т.е. да, иметь этап статитческого анализа, когда все старые предупреждения или запихнуты под коврик (помечены как FP или Intentional) на этапе до компиляции (или одновременно) - было бы полезно.
Но. Оно только тогда полезно, когда этот этап проходит быстро, а не в *десятки* раз медленнее обычной компиляции.
Кроме того, в реальной жизни многих заставят быть рабом машины - тратить время на устранение предупреждений анализатора, даже если они intentional или FP. Ну как во многих местах разработчиков заставляют избавляться от всех предупреждений компилятора.
К чему это приводит - мы видели в истории с Debian и OpenSSL.
Большинство ползователей
Большинство ползователей Коверити гоняют его в ночном билде а не на своем компе, так что скорость (которая обычно 3-5Х) разработчика волновать не должна. И кстати, если ты гоняешь на своем компе, билд инкрементальный.
На счет рабства - в реальной жизни кто-то должен определить какие дефекты важны в твоей конторе а какие нет.. даже дефолтная конфигурация включает в себя далеко не все что тул умеет ловить, но и в ней может быть шум. То что важно надо фиксить, то что побоку - надо фильтровать. В отличии от линта или спарса тут это довольно легко сделать.
Про Clang тут уже говорили, а
Про Clang тут уже говорили, а про cppcheck вроде бы нет. Хорошая оупен-сорсная утилита для статического анализа.
Джон Кармак использует PVS Studio https://twitter.com/ID_AA_
Джон Кармак использует PVS Studio https://twitter.com/ID_AA_Carmack/status/224013523565555712
"в компактных проектах, которые ведутся очень небольшим коли
"в компактных проектах, которые ведутся очень небольшим количеством опытных девелоперов" ?
Ага, в Rage 20 человек в кредитах на разработку, немного бол
Ага, в Rage 20 человек в кредитах на разработку, немного больше чем нужно.
Тогда он же, в Армадилло Аероспейс: https:// twitter.com/ID_AA_Carmack/status/226800462890946561
Целую статью на эту тему написал еще:
http:// www.altdevblogaday.com/2011/12/24/static-code-analysis/
Я так понял, шумят они все, а не детские ошибки ловит только PVS (которая наша разработка).
LibRaw, Coverity SCAN, PVS-Studio
Прочитал заметку о проверке маленького проекта LibRaw с помощью Coverity SCAN. Из статьи следует, что ничего интересного не нашлось. Решил попробовать, сможет ли найти что-то анализатор PVS-Studio.
Читать далее: http://habrahabr.ru/company/pvs-studio/blog/211727/
Ага, спасибо, я там ответил.
Ага, спасибо, я там ответил.